Helvetic Coding Contest 2019 A2. Heidi Learns Hashing (Medium)(数论)

题目链接: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 x0x2=y0x1x3=y1x2x0=y2x3x1=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;
}

猜你喜欢

转载自blog.csdn.net/toohandsomeIeaseId/article/details/104451884