bzoj1411: [ZJOI2009]硬币游戏

Description

Orez很喜欢玩游戏,他最近发明了一款硬币游戏。他在桌子的边缘上划分出2*n个位置并按顺时针把它们标号为1,2,……,2n,然后把n个硬币放在标号为奇数的位置上。接下来每次按如下操作:在任意两个硬币之间放上一个硬币,然后将原来的硬币拿走;所放硬币的正反面由它两边的两个硬币决定,若两个硬币均为正面朝上或反面朝上,则所放硬币为正面朝上,否则为反面朝上。 那么操作T次之后桌子边缘上硬币的情况会是怎样的呢?

Input

文件的第一行包含两个整数n和T。 接下的一行包含n个整数,表示最开始桌面边缘的硬币摆放情况,第i个整数ai表示第i个硬币摆放在2*i-1个位置上,ai=1表示正面朝上,ai=2表示反面朝上。

Output

文件仅包含一行,为2n个整数,其中第i个整数bi桌面边缘的第i个位置上硬币的情况,bi=1表示正面朝上,bi=2表示反面朝上,bi=0表示没有硬币。

Sample Input

10 5

2 2 2 1 1 1 1 1 1 2

Sample Output

0 1 0 1 0 1 0 1 0 2 0 1 0 2 0 1 0 1 0 1

数据范围

30%的数据 n≤1000 T≤1000

100%的数据 n≤100000 T≤2^60

思路

思路1

我们可以先用O(mn)的模来做一做,之后可以发现每过2^k次方后,每个硬币都有规律的,我们就可以这样相当于拆一下二进制就可以了。

思路2

我们只考虑偶数的行,易知第二行每个数是原序列该位置左右两个数的异或
由数学归纳法可以 第2^k行每个数是原序列该位置左侧第2^(k-1)个数和右侧第2^(k-1)个数的异或
然后将T进行二进制拆分,每位进行一次变换即可 最后再讨论T的奇偶
时间复杂度O(n*logT)

ps:这题有毒,最后没有空格,我第一次就输出了空格结果PE了
ps:还有,在位运算时要把1变成long long否则会炸
哎~~,害得我这题三遍才过
这里写图片描述

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
    int ret=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    while(c>='0'&&c<='9')ret=ret*10+c-'0',c=getchar();
    return ret*f;
}
int n,T,a[200005],b[200005];
#undef int 
int main(){
#define int long long
    n=read();T=read();
    for(int i=1;i<=n;++i)a[i*2-1]=read();
    if(T&1){
        for(int i=1;i<n;++i)a[2*i]=(a[2*i-1]!=a[2*i+1])+1;
        a[2*n]=(a[1]!=a[2*n-1])+1;
        for(int i=1;i<=n;++i)a[2*i-1]=0;
        --T;
    }
    for(int i=1;i<=62;++i){
        if(!((T>>i)&1))continue;
        for(int j=1;j<=2*n;++j){
            if(!a[j])continue;
            int l=(j-((int)1<<i))%(2*n);
            l=(l+i*2*n)%(2*n);
            if(!l)l=2*n;
            int r=(j+((int)1<<i))%(2*n);
            if(!r)r=2*n;
            b[j]=(a[l]!=a[r])+1;
        }
        for(int j=1;j<=2*n;++j)a[j]=b[j];
    }
    for(int i=1;i<2*n;++i)printf("%lld ",a[i]);
    printf("%lld",a[2*n]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章