这题细节真的多。。但是思维性还是蛮强的
一个数最多被操作logn次,所以可以预处理第一次操作,然后暴力解决
分类讨论下修改1的情况和修改0的情况,处理出前缀和和后缀和即可
#include<bits/stdc++.h> using namespace std; #define N 200005 char a[N]; int n,num,x,y,modx[N],mody[N],ans[N],pre[N],suf[N]; int calc(int x){//把x除到0需要的步数 int res=0; while(x){ res++; int tot=0; for(int i=0;(1<<i)<=x;i++) tot+=(x>>i & 1); x%=tot; } return res; } int main(){ cin>>n; cin>>(a+1); for(int i=1;i<=n;i++) if(a[i]=='1')num++; if(num==0){//没有1的时候 for(int i=1;i<=n;i++)cout<<1<<' '; return 0; } if(num==1){//只有一个1的时候 if(a[n]=='1') for(int i=1;i<=n;i++)ans[i]=(i==n?0:2); else for(int i=1;i<=n;i++){ if(a[i]=='1')ans[i]=0; else if(i==n)ans[i]=2; else ans[i]=1; } for(int i=1;i<=n;i++)cout<<ans[i]<<' '; return 0; } x=num+1;y=num-1;//y>=1 // 先把操作一次得到的结果求出来 long long base=1; for(int i=n;i>=1;i--){ base%=x; modx[i]=base; base*=2; } for(int i=1;i<=n;i++){ if(a[i]=='1')pre[i]=modx[i]+pre[i-1]; else pre[i]=pre[i-1]; pre[i]%=x; } for(int i=n;i>=1;i--){ if(a[i]=='1')suf[i]=modx[i]+suf[i+1]; else suf[i]=suf[i+1]; suf[i]%=x; } for(int i=1;i<=n;i++)if(a[i]=='0'){ ans[i]=pre[i-1]+suf[i+1]+modx[i]; ans[i]%=x; } base=1; for(int i=n;i>=1;i--){ base%=y; mody[i]=base; base*=2; } for(int i=1;i<=n;i++){ if(a[i]=='1')pre[i]=mody[i]+pre[i-1]; else pre[i]=pre[i-1]; pre[i]%=y; } for(int i=n;i>=1;i--){ if(a[i]=='1')suf[i]=mody[i]+suf[i+1]; else suf[i]=suf[i+1]; suf[i]%=y; } for(int i=1;i<=n;i++)if(a[i]=='1'){ ans[i]=pre[i-1]+suf[i+1]; ans[i]%=y; } for(int i=1;i<=n;i++) cout<<calc(ans[i])+1<<' '; }