bzoj1411 硬币游戏 分治

来源:互联网 发布:k核分析 社会网络 编辑:程序博客网 时间:2024/06/06 21:03

        显然如果令正面=0,反面=1,那么中间硬币的值为两边硬币的异或。然后我们来找规律>_<:

       我们首先假设这个序列为a[1],0,a[3],0,a[5],0,a[7],0,a[9],0,a[11],...,然后把每一次的值弄出来(0省略不写,假设a[1]左边是a[-1],a[-1]左边是a[-3]方便找规律):

       a[1] a[3] a[5] a[7] a[9] a[11]

       a[-1]^a[1] a[1]^a[3] a[3]^a[5] a[5]^a[7] a[7]^a[9] a[9]^a[11]

       a[-3]^a[1] a[-1]^a[3] a[1]^a[5] a[3]^a[7] a[5]^a[9] a[7]^a[11] a[9]^a[13]

      然后我们发现翻两次之后有:a[i]=a[i-2]^a[i+2],那么我们不妨设不为空的序列比如a[1],a[3],a[5],a[7]把它压成a[1],a[2],a[3],a[4],接下来一次翻两步,就变成:

       a[1] a[2] a[3] a[4]

       a[0]^a[2] a[1]^a[3] a[2]^a[4] a[3]^a[5]

       翻四步第的结果,相当于再翻两下,变成:

       a[0]^a[2] a[1]^a[3] a[2]^a[4] a[3]^a[5]

       a[-2]^a[4] a[-1]^a[5] a[1]^a[5] a[2]^a[6]。

       类似的就可以得到翻8,16,32,64步之后的结果,就可以发现对于任意得k,我们得到翻了2^k以后的结果为

       a[i]=a[i-2^(k-1)]^a[i+2^(k-1)]。可以用归纳法证明。

       然后就可以根据T的二进制位进行分治了,用类似于快速幂的方式即可。注意当T为奇数时可以先翻一步,然后T=T-1。

AC代码如下:

#include<iostream>#include<cstdio>#include<cstring>#define ll long long#define N 100005using namespace std;int n,a[2][N]; ll m;int main(){scanf("%d%lld",&n,&m); int i,k,flag=0;for (i=1; i<=n; i++){scanf("%d",&a[0][i]); a[0][i]--;}if (m&1){m--; flag=1; a[0][n+1]=a[0][1];for (i=1; i<=n; i++) a[0][i]^=a[0][i+1];}int last=0,now=0;for (m>>=1,k=1; m; m>>=1,k=(k<<1)%n)if (m&1){last=now; now^=1;int x=(n-k)%n+1,y=k+1;for (i=1; i<=n; i++){a[now][i]=a[last][x]^a[last][y];x=x%n+1; y=y%n+1;}}if (flag) printf("0 ");for (i=1; i<n; i++) printf("%d 0 ",a[now][i]+1);if (flag) printf("%d\n",a[now][n]+1); else printf("%d 0\n",a[now][n]+1);return 0;}


by lych

2016.2.27

0 1
原创粉丝点击