P3235 [HNOI2014] 江南乐

题意

\(T\) 组数据,每组数据给 \(n\) 堆石子,每次操作者选择一堆不少于 \(F\) 个石子,和一个整数 \(M\),并把这堆石子尽量均分为 \(M\) 份,问后手是否必胜。
\(T,n<100\)\(F<10^5\),每堆石子数量 \(<10^5\)

思路

利用总局面的 \(SG\) 值等于子局面的 \(SG\) 值得异或和,当前局面的 \(SG\) 值等于后继状态的 \(SG\) 值的 \(mex\) 来计算。
设现在要计算一堆 \(x\) 个石子的 \(SG\) 值,考虑将祂分成 \(m\) 份,设 \(t=\lfloor\frac xm\rfloor\),那么分出来的堆只有可能是 \(t\) 个或 \(t+1\) 个。
考虑用整除分块优化。找出 \(l,r\) 使得 \(\forall i\in[l,r],\lfloor\frac xi\rfloor=t\),分类讨论一下,求出 \(SG\) 值,最后去 \(mex\)
由于 \(F\) 是固定的,可以预处理,查询 \(\mathcal O(n)\)

代码

// Problem: P3235 [HNOI2014] 江南乐
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3235
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
const int maxf=100010;
int T,F,sg[maxf];
vector<int>mins;
bool vis[maxf];
void mminus(int x){vis[x]=1,mins.push_back(x);}
int get(){
    int u=0;
    while(vis[u]) u++;
    return u;
}
void clear(){for(int i:mins) vis[i]=0;mins.clear();}
void solve(){
    int n;read(n);
    int SG=0;
    for(int i=1;i<=n;i++){
        int x;read(x);
        SG^=sg[x];
    }
    write(!!SG);
}
signed main(){
    read(T,F);
    if(F==1) F=2;
    for(int x=F;x<=100000;x++){
        for(int l=1,r;l<=x;l=r+1){
            r=x/(x/l);
            int t=x/l;
            if(l==r){
                int val=0;
                if(x%l&1) val^=sg[t+1];
                if(l-x%l&1) val^=sg[t];
                mminus(val);
                continue;
            }
            if(x&1) mminus(sg[t]^sg[t+1]);
            if(x&1){
                if(t&1) mminus(sg[t]);
                else mminus(sg[t+1]);
            }
            else{
                if(t&1) mminus(sg[t+1]);
                else mminus(sg[t]);
            }
        }
        sg[x]=get();clear();
    }
    while(T--) solve(),write(" ");
    return 0;
}
posted @ 2026-07-12 15:45  Link-Cut_Trees  阅读(8)  评论(1)    收藏  举报