這題是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;
}
下面是題解的證明:(大佬的做法。。。)