[Brute force][规律] Jzoj P3084 超级变变变
题解
- 首先,我们可以把所有数转换成二进制来看
- 那么两个操作,一个显然就是把二进制下最后一位的1变成0,一个就是将二进制右移一位
- 这样每次修改的只可能是最后一个数,这样的话我们就可以想到,对于一个数若可以变成k
- 那么它在二进制下k一定是它的一个从1开始的子串(前继)
- 想到这里的话,其实就很容易做了
- 这题貌似还有一种极其暴力的算法,而跑的贼快
- 对于一个偶数,那么它可以拓展的就是2*n,2*n+1然后这两个数又可以往下拓展
- 暴力碾表算!!!
代码
1 #include <cstdio> 2 #include <cstring> 3 #define ll long long 4 using namespace std; 5 ll k,A,B,mi[640]; 6 int tot[640],num[640]; 7 ll calc(ll x) 8 { 9 if (x<k) return 0; 10 if (x==k) return 1; 11 memset(tot,0,sizeof(tot)); 12 ll l=x,ans=0,p,q; 13 while (l>0) tot[++tot[0]]=l%2,l/=2; 14 for (int i=num[0];i<tot[0];i++) ans+=mi[i-num[0]]; 15 for (int i=1;i<=tot[0];i++) 16 { 17 if (i>num[0]) p=0; else p=num[num[0]-i+1]; 18 q=tot[tot[0]-i+1]; if (p>q) break; 19 if (i<=num[0]&&q>p) { ans+=mi[tot[0]-num[0]]; break; } 20 if (i>=num[0]&&q>p) ans+=mi[tot[0]-i]; 21 if (i==tot[0]&&q>=p) ans++; 22 } 23 return ans; 24 } 25 int main() 26 { 27 scanf("%lld%lld%lld",&k,&A,&B),mi[0]=1; 28 for (int i=1;i<=63;i++) mi[i]=mi[i-1]*2; 29 if (k==0) { printf("%lld",B-A+1); return 0; } 30 if (B<k) { printf("0"); return 0; } 31 if (k%2==0) k/=2; ll l=k; 32 while (l>0) num[++num[0]]=l%2,l/=2; 33 printf("%lld",calc(B)-calc(A-1)); 34 }