[BZOJ]5000: OI树 倍增

来源:互联网 发布:综漫 收集数据做主神 编辑:程序博客网 时间:2024/06/14 13:36

Description

几天之后小跳蚤即将结束自己在lydsy星球上的旅行。这时,lydsy人却发现他们的超空间传送装置的能量早在小跳
蚤通过石板来到lydsy星球时就已经消耗光了。这时,小跳蚤了解到自己很有可能回不到跳蚤国了,于是掉下了伤
心的眼泪……lydsy人见状决定无论如何也要送小跳蚤回地球,于是lydsy人的大祭司lavendir决定拜访lydsy星球
的OI树,用咒语从OI树中取得能量。咒语中有K种字母,我们用前K个大写英文字母来表示它。OI树可以被认为是一
个有着N个节点的带权有向图,所有节点的出度都是K,并且所有的出边都对应于一个咒语中的字母。仪式开始的时
候有一个标记物放在OI树的1号节点上。之后,从咒语的第一个字母开始,每经过一个字母,标记物就沿着该字母
对应的出边进入这条边的终点,并且得到相当于边权大小的能量值。当咒语处理完毕时,就可以得到这个过程中得
到的所有能量了。现在由于lydsy人超群的计算能力,他们已经知道某咒语大概会获得多少能量,只是还想知道会
获得的能量值对一个数M取模的结果。跳蚤国王通过小跳蚤留下的石板也了解到了小跳蚤现在的处境,所以他又找
到了你,希望你帮助他计算出这个问题的答案。

题解:

tkj大神告诉我是倍增之后,就很简单了。to[i][j][k]表示从i走第j个字母,走2k步到达的点;v[i][j][k]表示从i走第j个字母,走2k步后获得的权值。

代码:

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int maxn=10010;char str[120010];int n,k,mod,ans=0,a[maxn][28],b[maxn][28],to[maxn][28][32],v[maxn][28][32];int getv(int x,int y,int z){    if(z==0)return 0;    if(z==1)return v[x][y][0];    for(int i=30;i>=0;i--)    if(z>=(1<<i))return (v[x][y][i]+getv(to[x][y][i],y,z-(1<<i)))%mod;}int getto(int x,int y,int z){    if(z==0)return x;    if(z==1)return to[x][y][0];    for(int i=30;i>=0;i--)    if(z>=(1<<i))return getto(to[x][y][i],y,z-(1<<i));}int main(){    scanf("%d%d",&n,&k);    for(int i=1;i<=n;i++)    for(int j=1;j<=k;j++)    {        scanf("%d%d",&a[i][j],&b[i][j]);        to[i][j][0]=a[i][j];        v[i][j][0]=b[i][j];    }    scanf("%s%d",str,&mod);    for(int l=1;l<=30;l++)    for(int i=1;i<=n;i++)    for(int j=1;j<=k;j++)    {        v[i][j][l-1]%=mod;        to[i][j][l]=to[to[i][j][l-1]][j][l-1];        v[i][j][l]=(v[i][j][l-1]+v[to[i][j][l-1]][j][l-1])%mod;    }    int now=1,len=strlen(str);    for(int i=0;i<len;)    {        if(str[i]==']'){i++;continue;}        if(str[i]=='[')        {            int j,num=0;            for(j=i+1;;j++)            {                if(str[j]<'0'||str[j]>'9')break;                num=num*10+str[j]-'0';            }            ans=(ans+getv(now,str[j]-'A'+1,num))%mod;            now=getto(now,str[j]-'A'+1,num);            i=j+2;        }        else if(str[i]>='A'&&str[i]<='Z')        {            ans=(ans+getv(now,str[i]-'A'+1,1))%mod;            now=getto(now,str[i]-'A'+1,1);            i++;        }    }    printf("%d",ans);}