1.暴走的猴子(walk.pas/c/cpp)
【题目描述】
从前有一个森林,森林里生活着一群猴子,这里猴子有个恶趣味——暴走。现在给你这个森林里的树木描述,你能计算出这只猴子在暴走k步后会蹦达到哪里吗(友情提示:由于你上周帮助猎人写程序打死了猴子父亲,所以今天猴子特别不爽,故意暴走了很多很多步来为难你,从而导致了k非常的大,做好心里准备噢~)
【输入数据】
第一行两个数n,m表示树木数和询问次数
接下来n行,第i个数一个数ai表示这只猴子当前在第i棵树的话,下一步会走到第ai棵树
接下来m行,每行两个数t,k,询问如果当前猴子在第t棵树,k步之后它会到第几棵树
【输出数据】
m行为每次询问的结果
【样例输入】
3 2
2
3
2
1 2
2 4
【样例输出】
3
2
【数据范围】
共十个测试点,每个测试点数据规模如下所示
1.n=10^2,m=n,k<=10^2
2.n=10^3,m=n,k<=10^3
3.n=10^4,m=1,k<=10^9
4.n=10^5,m=1,k<=10^9
5.n=10^5,m=1,k<=10^12
6.n=10^5,m=1,k<=10^15
7.n=10^5,m=1,k<=10^18
8.n=10^5,m=n,k<=10^12
9.n=10^5,m=n,k<=10^15
10.n=10^5,m=n,k<=10^18
【时限】
1s
因为任何数都可以分为2的几次方相加(eg:17=2^0+2^3+2^3),所以这道题直接倍增就能优化。
还有几个特别重要的需要注意的地方!代码里都注释了我这里就不再写一次啦,啦啦啦啦啦
欢迎代码君上场▔▽▔
#include<cstdio>//动归加优化
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=100000+10;
int n,m;
int a[maxn];
int f[maxn][100];//f[i][j]从i开始跳2^j步到达的位置
int t;
long long k;//因为k的值很大,所以要用long long
int main()
{
//freopen("walk.in","r",stdin);
//freopen("walk.out","w",stdout);
scanf("%d%d",&n,&m);//输入树木数(n)和询问次数(m)
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
f[i][0]=a[i];;//初始
}
for(int j=1;j<=60;j++)//这里尤其要注意!!j的枚举应该是在外层,因为每移动一次改变的是步数
{//另外因为题目数据为10^18大约等于2^60,所以这里取60
for(int i=1;i<=n;i++)
{
f[i][j]=f[f[i][j-1]][j-1];
}
}
for(int i=1;i<=m;i++)
{
int sum=0;
scanf("%d%I64d",&t,&k);//注意是用l64d,如果用lld就无法读完数
while(k>=1)//------------↓↓难点分割线↓↓------------------------------
{
if(k%2!=0) t=f[t][sum];
k=k/2;
sum++;
}
printf("%d\n",t);
}
return 0;
}
2.划分数列(seq.pas/c/cpp)
【题目描述】
给你一个有n个元素的数列,要求把它划分成k段,使每段元素和的最大值最小
【输入格式】
第一行两个正整数n,k
第二行为此数列ai
【输出格式】
一行一个数,为题目所求答案
【样例输入】
5 2
2 1 3 4 5
【样例输出】
9
【数据规模】
30%数据 n <= 30, k <= 10
100%数据 n <= 100000, k <= n, ai<= 10^9
150%数据 n <= 100000, k <= n, |ai|<= 10^9(附:这50分超越了noip难度,大家可以无视)
【时限】
1s
记得以前做过一道dp的题和这道题差不多,有点不同的就是。。_(:з」∠)_数据太大了啊。。。
所以我们当然就要对它进行优化啦,这里我就用二分进行了优化。
热烈欢迎代码君└( ̄  ̄└)(┘ ̄  ̄)┘
---------------------------------------------代码君出场的分割线------------------------------------------------------------------------
#include<cstdio>//二分加dp
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100000+10;
int n,k;
int a[maxn];
int sum;//对每段的和进行二分
int l,r,mid;//二分:左端点(l),右端点(r),中间的点(mid).
bool judge(int mid)//一个表判定的函数
{
int num;//记录当前总和 //s
int i=0;//点
int s=0;//记录划分的段数//num
while(true)
{
num=0;
while(true)
{
if(i>=n)//没有需要操作的点了
{
s++;
break;
}
if(num+a[i+1]<=mid)
{
num+=a[i+1];
i++;
}
else
{
s++;
break;
}
}
if(i>=n) break;
}
if(s<=k)return true;//如果不够或刚好分m个部分,返回true
else return false;//如果多分了就返回false
}
int main()
{
freopen("seq.in","r",stdin);
freopen("seq.out","w",stdout);
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
l=1;
r=sum;
while(l<r)//即还没找到,继续二分
{
mid=l+(r-l)/2;//---------------↓↓重点理解分割线↓↓-------------------
if(judge(mid)) r=mid;
else l=mid+1;
}
printf("%d",r);
return 0;
}
3.无聊的游戏(boring.pas/c/cpp)
【题目描述】
有一个很无聊的游戏,就是——根据递推公式计算数列
没错,这道题就是这么无聊!
给你数列f(0)=1, f(n)=f(n-1)^2+1 (n > 0)
求出f(n)
(既然题目已经这么无聊了,那就不让大家写高精度了,取个模好了)
【输入格式】
一个整数n
【输出格式】
一行一个整数f(n),结果对1200007取模
【样例输入】
3
【样例输出】
26
【数据规模】
10%数据 1<=n<=10
30%数据 1<=n<=10^6
100%数据 1<=n<=10^9
【时限】
1s
打表加模拟或者找循环节,只是找循环节的话还要再另写一个程序,这道题重点还是要找出其中的循环节,具体过程在代码中。
#include<cstdio>//t3
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=1200007;
/*
这道题是用打表和模拟;
循环节已找出:从803开始,每隔570为一个循环节
*/
int n;
long long f[1500];//***最开始因为我定义成int 导致WA了很多次啊orz
int main()
{
//freopen("boring.in","r",stdin);
//freopen("boring.out","w",stdout);
scanf("%d",&n);
f[0]=1;//这里的初始化很重要
for(int i=1;i<=1500;i++)
{
f[i]=((f[i-1]*f[i-1])%mod+1)%mod;//因为最后要对mod取余,所以如果每计算一次就取一次余,能够降低复杂度(注意!要双重mod)
if(i==n)
{
printf("%d",f[n]);//此时已求出f[n]所以直接输出
return 0;
}
}
n=(n-802)%570;
if(n==0) n+=570;//如果n恰好为循环节节点
n+=802;
printf("%d",f[n]);
return 0;
}