这题是Mutli-SG 的变形。
本质还是SG函数。
不考虑算法复杂度来看:
每个局面的SG值等于只有一个硬币的SG值的异或和。
我们这样想:把长度为n的字符串当成n堆石子。
从左往右第i位为1表示第i堆有i个石子。
一次操作相当于把第x堆石子分成 若干堆(小于第x堆大小)。(会出现某一位有2个正面朝上的硬币,即2个堆大小相同的堆,但是他们SG的异或和为0,相当于消去他们,即变成背面朝上)
有了上面的转化,我们就能用SG函数求解了。
暴力打出SG函数的表是n*n*k的复杂度。
正常人的做法:打表发现,SG[i]=min(lowbit(i),2^w); 2^w<=k<2^(w+1)
然后O1打出SG,直接搞即可。
下面程序附带打表
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y){ee[++cnt].nxt=head[x],ee[cnt].to=y,head[x]=cnt;}
*/
int SG[M];
int vs[M];
char s[M];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,k;
cin>>n>>k;
// SG[1]=1;//只有一个SG==0的可达
// for(int i=2;i<=100;i++)
// {
// memset(vs,0,sizeof(vs));
// vs[0]=1;//翻一枚硬币
// for(int j=2;j<=k;j++)//翻几枚硬币
// {
// if(i-j+1<1)break;
// int tp=0;
// for(int q=i-j+1;q<i;q++)tp^=SG[q];
// vs[tp]=1;
// }
// for(int j=1;j<=100;j++)if(!vs[j]){
// SG[i]=j;
// break;
// }
// }
// for(int i=1;i<=100;i++)
// {
// cout<<i<<" "<<SG[i]<<" "<<(i&(-i))<<endl;
// }
int ans=1;
while(k>1)
{
k/=2;
ans*=2;
}
for(int i=1;i<=n;i++)
SG[i]=min(-i&i,ans);
cin>>s;
int tp=0;
for(int i=1;i<=n;i++)
{
if(s[i-1]=='1')tp^=SG[i];
}
if(tp==0)cout<<"No"<<endl;
else cout<<"Yes"<<endl;
return 0;
}
下面是题解的证明:(大佬的做法。。。)