[HAOI2015]數組遊戲

一、題目

點此看題

二、解法

這類翻棋子游戲有一個套路,可以把原遊戲看成翻每一個棋子的組合遊戲。

想要求出每一個子遊戲的sgsg函數值,轉移時可以繼續把後面的棋子拆成若干個子游戲,設sg[i]sg[i]爲第ii個位置的sgsg函數,轉移就是sgsg函數前綴和。

打表可以發現ni\frac{n}{i}取值相同時sgsg函數值相同,其實這也不難意會,因爲ni\frac{n}{i}相同,所以後面能翻棋子的分佈是差不多的,這就導致遊戲會產生相同的結果(都說是意會,這麼認真幹嘛)

考慮分塊做法ni\frac{n}{i}相同的劃分成一塊,再用n\sqrt n分成兩部分考慮,小於的部分直接存,大於的部分存ni\frac{n}{i}。具體轉移的時候只需要考慮每個塊,設這個塊爲xx,假設已經確定了左端點ll,可以先找到ni\frac{n}{i}相等的右端點,然後就可以算真正的右端點rr(滿足rr整除於xx),設sgsg函數的前綴和爲resres,轉移塊的sgsg函數爲zz,那麼res xor zres\space xor\space z是一定取得到,這時候我們還要考慮l,rl,r中能被xx整除的個數,如果爲奇數就把前綴和異或上zz

做法的複雜度作者不是特別清楚,但是隻要注意卡常,還是足以通過此題。

#include <cstdio>
#include <cmath>
const int M = 500005;
int read()
{
 int x=0,flag=1;char c;
 while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
 while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
 return x*flag;
}
int n,m,k,t,nn,a[M],b[M],q[M],g[M],tag[M];
void init()
{
    for(int l=1,r;l<=n;l=r+1)
        r=n/(n/l),q[++t]=r;
    for(int i=t;i>=1;i--)
    {
        int x=q[i],res=0,cnt=0,y=1;
        for(int l=2*x,r;l<=n;l=r+x)
        {
            r=n/(n/l)/x*x;
            int z=l<nn?a[l]:b[n/l];
            tag[res^z]=1;
            g[++cnt]=res^z;
            if(((r-l)/x+1)&1) res^=z;
        }
        while(tag[y]) y++;
        x<nn?a[x]=y:b[n/x]=y;
        for(int j=1;j<=cnt;j++) tag[g[j]]=0;
    }
}
int main()
{
    n=read();m=read();
    nn=sqrt(n);
    init();
    while(m--)
    {
        k=read();
        int res=0;
        for(int i=1;i<=k;i++)
        {
            int x=read();
            res^=(x<nn?a[x]:b[n/x]);
        }
        if(res) puts("Yes");
        else puts("No");
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章