AIsing Programming Contest 2020 D题解

题目链接:https://atcoder.jp/contests/aising2020/tasks/aising2020_d

题目大意:给一个长度为n的01串,对于i位置进行反转(0转1,1转0);
f(x): x转换成2进制时1的个数y,x=x%y,当x==0时循环的次数。
求解i(0~n-1)将i反转后01串转换成10进制x,求f(x)的值。

思路:01串总长度为2e5,故能想到取一次%mod后这个值一定在2e5范围内。然后就可以暴力求得结果了。问题是怎么去取第一次mod呢?首先我们预处理1的个数为one,从高位逐步*2+该位的值%mod。显然每次处理情况是one+1或者one-1,所以mod等于one-1或者one+1,因为要重复计算,所以首先把整体计算出来,后面只需要-2i或者+2i
注意点:1.在计算过程中有个大坑就是1的个数会变成0的情况这个需要特判。
2.快速幂要开long long。

然后我们可以愉快的ac了。(细节看代码)

#include 
using namespace std;
typedef long long ll;
const int N=1e6+100;

ll qpow(ll x,ll n,int mod)
{
    ll sum=1;
    while(n){
        if(n&1) sum=sum*x%mod;
        x=x*x%mod;
        n>>=1;
    }
    return sum;
}
int ans(int x)
{
    if(x==0) return 0;
    int kk=__builtin_popcount(x);
    return 1+ans(x%kk);
}
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    int n;cin>>n;
    string s;cin>>s;
    int one=0;
    for(int i=0;i<n;i++){
        if(s[i]=='1') one++;
    }
    int p1=0,p2=0;
    for(int i=0;i<n;i++){
        if(one!=1){
            p1=(p1*2+s[i]-'0')%(one-1);
        }
        p2=(p2*2+s[i]-'0')%(one+1);
    }
    for(int i=0;i<n;i++){
        if((s[i]=='1'&&one==1)){
            cout<<0<<endl;
        }
        else if(s[i]=='1'){
            cout<<1+ans((p1-qpow(2,n-i-1,one-1)+(one-1))%(one-1))<<endl;
        }
        else if(s[i]=='0'){
            cout<<1+ans((p2+qpow(2,n-i-1,one+1)+(one+1))%(one+1))<<endl;
        }
    }
    return 0;
}

欢迎大家来指错。

你可能感兴趣的:(AIsing Programming Contest 2020 D题解)