【动规递推】【120814测试】【NOIP第二次模拟赛】暴走的猴子

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

 

①由于每棵树都有跳到的下一棵树的编号,所有从当前这棵树开始跳,一定会从某时刻进入循环节,用寻找循环节的方法可以拿到70分。


②说这个方法之前,先说一下:任意一个数总可以拆成2的几次方相加,如31=20+22+24

    一个非常好的方法是倍增f[k,i]代表从i开始跳2^k步会到哪里,初始
      f[0,i]=next[i]

      f[k,i]=f[k-1,f[k-1,i]]

    复杂度m*log(n);

 

C++ Code

#include<cstdio>
#define MAXN 100000+10

int n,m,next[MAXN],f[MAXN][100];

bool ji(int x)
{
    if(x%2==0)return false;else return true;
}

int main()
{
    freopen("walk.in","r",stdin);
    freopen("walk.out","w",stdout);
    scanf("%d%d",&n,&m);
    int i;
    for(i=1;i<=n;i++)
    {
        scanf("%d",&next[i]);
    }
    for(i=1;i<=n;i++)f[i][0]=next[i];
    int j;
    for(j=1;j<=60;j++)
        for(i=1;i<=n;i++)
            f[i][j]=f[f[i][j-1]][j-1];
    int t,sum=0;
    long long k;
    for(i=1;i<=m;i++)
    {
        sum=0;
        scanf("%d%I64d",&t,&k);
        while(k!=0)
        {
            if(ji(k)) t=f[t][sum];
            k=k/2;
            sum++;
        }
        printf("%d\n",t);
    }
    return 0;
}

 

 

 

posted @ 2012-08-20 19:46  jiangzh  阅读(198)  评论(0编辑  收藏  举报