C. Travelling Salesman and Special Numbers(数位)

    技术2022-07-11  123

    首先因为二进制最多1000位,操作一次后数就变成了[1,1000]的数

    所以要求数字x满足操作k次回到1

    其 实 就 是 枚 举 [ 1 , 1000 ] 中 操 作 次 数 是 k − 1 次 的 数 i 其实就是枚举[1,1000]中操作次数是k-1次的数i [1,1000]k1i

    然 后 计 算 二 进 制 有 i 个 1 且 小 于 原 数 字 的 有 多 少 个 累 加 即 可 然后计算二进制有i个1且小于原数字的有多少个累加即可 i1

    前面思维难度都不大,关键是如何找有i个1且小于原数字的数量?

    不 妨 我 们 把 最 长 公 共 前 缀 记 作 j , 原 数 字 n 长 度 记 作 l e n \color{Red}不妨我们把最长公共前缀记作j,原数字n长度记作len j,nlen

    j ∈ [ 1 , l e n ] , 那 么 前 j 位 都 相 同 , 第 j + 1 位 是 0 且 n 的 第 j + 1 位 是 1 j\in[1,len],那么前j位都相同,第j+1位是0且n的第j+1位是1 j[1,len],j,j+10nj+11

    我 们 就 这 样 去 枚 举 它 的 公 共 前 缀 , 计 算 前 缀 前 1 的 数 我们就这样去枚举它的公共前缀,计算前缀前1的数 ,1

    然 后 第 [ j + 2 , l e n ] 的 二 进 制 就 可 以 任 意 , 组 合 数 来 计 算 然后第[j+2,len]的二进制就可以任意,组合数来计算 [j+2,len],

    #include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1009; const int mod=1e9+7; using namespace std; ll c[maxn][maxn],dp[maxn],k,pre[maxn]; char a[maxn]; int calucate(int x){ int cnt=0; while(x!=1){ int temp=0; while(x) { if(x&1) temp++; x>>=1; } x=temp,cnt++; } return cnt; } void init() { c[0][0]=1; for(int i=1;i<=1000;i++) { c[i][0]=1; for(int j=1;j<=1000;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; } } int main() { cin >> (a+1) >> k; if(k==0) cout<<1; else if(k==1) cout<<strlen(a+1)-1; else { init();//求组合数 int len=strlen(a+1),num=0; for(int i=1;i<=len;i++) pre[i] = pre[i-1]+(a[i]=='1'); dp[ pre[len] ] = 1; for(int i=0;i<len;i++)//公共前缀长度 { if(a[i+1]=='0') continue; //让第i+1位变成0,那么现在已经更小了,[i+2,len]是任意的 int num=pre[i],tot=len-(i+2)+1; for(int bit=0;bit<=tot;bit++)//放bit个1 dp[bit+num]=(dp[bit+num]+c[tot][bit])%mod; } ll ans=0; for(int i=1;i<=len;i++) if(calucate(i)==k-1) ans=(ans+dp[i])%mod; cout<<ans; } }
    Processed: 0.020, SQL: 10