BZOJ 1411硬币游戏(递推)

省选题。。

点击打开链接

原地址:点击打开链接

题目大意:

    N个硬币放在一个有2*N个位置的圆桌上,求T次操作后的情况。对于一个操作,如果两边都是正或都是负,则在中间放一个负,否则放一个正。

【思路】

    把正设为0,负设为1,则一个硬币的状态为两边硬币的抑或。

    把两次操作看作一次,则一次操作后硬币只有状态发生改变而位置不会改变。       

  通过数学归纳法得到:一个硬币的状态在操作2^k后是其左右两边与其相距2^k的硬币的抑或。直观的看,就是中间的项都被抑或消掉了。

    将T/2进行二进制拆分,不断进行操作即可。最后考虑T的奇偶性。


大佬的思路是真的跳。


#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define FOR(a,b,c) for(int a=(b);a<=(c);a++)
using namespace std;

typedef long long ll;
const int N = 5e5+10;

ll read() {
    char c=getchar();
    ll f=1,x=0;
    while(!isdigit(c)) {
        if(c=='-') f=-1; c=getchar();
    }
    while(isdigit(c))
        x=x*10+c-'0',c=getchar();
    return x*f;
}

int n; ll m;
int a[N],ans[N];

int main()
{
    //freopen("in.in","r",stdin);
    //freopen("out.out","w",stdout);
    n=read(),m=read();
    FOR(i,1,n) ans[i]=read(),ans[i]--;
    ll x=m/2;
    for(ll p=1;p<=x;p<<=1) if(x&p) { //这句表示p!=2*x+1,即仅比2^k大1
        memcpy(a,ans,sizeof(int)*(n+1));
        ll k=p%n;
        for(int i=1;i<=n;i++) {
            ll l=(i-1-k+n)%n+1,r=(i-1+k)%n+1;
            ans[i]=a[l]^a[r];
        }	
    }
    ans[0]=ans[n],ans[n+1]=ans[1];
    if(m&1) FOR(i,1,n-1) printf("0 %d ",(ans[i]^ans[i+1])+1);//注意,是连着读取的,总数是n而不是2n
    else FOR(i,1,n-1) printf("%d 0 ",ans[i]+1);
    if(m&1) printf("0 %d",(ans[n]^ans[n+1])+1);
    else printf("%d 0",ans[n]+1);
    return 0;
}
以m=31为例,首先将m/2=15,进行4次异或运算(代表2^0+2^1+2^2+2^3+2^4,根据思路红字解得),因为m为奇数,还需要额外再进行一次操作。。偶数同理。

猜你喜欢

转载自blog.csdn.net/zxwsbg/article/details/80113793