显然如果令正面=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 100005 using 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