题目链接:https://codeforces.com/contest/1184/problem/A2
题目大意:
给y,问k取0~n-1的情况下,有几个k能满足找到一个x,用x异或x循环右移k位能得到y
题目思路:
想了半天没思路,然后18学弟带飞,orz。可以发现,比如现在有个x,分别是 x 0 x 1 x 2 x 3 x_0x_1x_2x_3 x0x1x2x3,如果k取2的话,得到的就是 x 2 x 3 x 0 x 1 x_2x_3x_0x_1 x2x3x0x1,那么它们异或想要是y的话就需要满足
x 0 ⊕ x 2 = y 0 x 1 ⊕ x 3 = y 1 x 2 ⊕ x 0 = y 2 x 3 ⊕ x 1 = y 3 x_0 \oplus x_2=y_0 \\x_1 \oplus x_3=y_1 \\x_2 \oplus x_0=y_2 \\x_3 \oplus x_1=y_3 x0⊕x2=y0x1⊕x3=y1x2⊕x0=y2x3⊕x1=y3
然后发现他们呈一个又一个的圈,如果假设 x 0 x_0 x0是1,那么很快他这个圈里的其他值都能得到,如果再转回来算到的 x 0 x_0 x0还是1说明没有冲突也就没有问题。想要转回来很明显走的路程肯定是n的倍数,同时一次能走k步,所以要走的距离就是 l c m ( n , k ) lcm(n,k) lcm(n,k),每次走k步,那么就是走了 l c m ( n , k ) k \frac{lcm(n,k)}{k} klcm(n,k)步,因为这几步经过的点都是不同的,所以就可以当成步长是 n ÷ l c m ( n , k ) k = g c d ( n , k ) n \div \frac{lcm(n,k)}{k}=gcd(n,k) n÷klcm(n,k)=gcd(n,k)。想要转一圈没有冲突,那么这一路上遇到的1的个数需要是偶数,所以就直接暴力,对于每一个步长就直接暴力走,直到绕回来,看看是不是所有圈都是偶数个1。如果是的话就说明这个步数是可以的。刚才发现右移是否能符合要求只跟gcd有关,所以只用求i和n的gcd,然后没求过的暴力求就行,一共也就根号个gcd,所以复杂度没问题。
以下是代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
const int MAXN = 2e5+5;
const int MAXM = 4e7+5;
const int MOD = 1e9+7;
int n;
bool vis[MAXN];
char s[MAXN];
int mark[MAXN];
bool solve(int x){
memset(vis,0,sizeof(vis));
rep(i,0,n-1){
if(vis[i])continue;
vis[i]=1;
int temp=s[i]-'0';
int pos=(i+x)%n;
while(!vis[pos]){
vis[pos]=1;
temp^=s[pos]-'0';
pos=(pos+x)%n;
}
if(temp)return 0;
}
return 1;
}
int main()
{
while(~scanf("%d%s",&n,s)){
memset(mark,-1,sizeof(mark));
int ans=0;
int flag=0;
rep(i,0,n-1){
if(s[i]=='1'){
flag=1;
break;
}
}
if(!flag)ans++;
rep(i,1,n-1){
int bu=__gcd(i,n);
if(mark[bu]==-1){
mark[bu]=solve(bu);
}
if(mark[bu])ans++;
}
printf("%d\n",ans);
}
return 0;
}