码迷,mamicode.com
首页 > 其他好文 > 详细

CF914C Travelling Salesman and Special Numbers

时间:2019-08-23 19:30:56      阅读:86      评论:0      收藏:0      [点我收藏+]

标签:pre   ott   lap   amp   cli   bit   nbsp   ret   travel   

题目描述

对于一个正整数x,我们定义一次操作是将其变为它二进制下“1”的个数,比如我们知道1310?=11012 ,而1101有三个"1",所以对13进行一次操作就会将其变为3。显而易见的是,对于一个正整数,我们在进行若干次操作后,一定会将其变为1。

给定n和k,其中n是在二进制下被给出,求出所有不大于n且将其变为1的最小操作次数为k的数的个数对10^9+7取模的结果。

1<=n<21000, 0<=k<=10000

题解

因为n在二进制下长度最大为1000,所以最多有1000个1,所以转化一次最多是1000。

我们可以处理出当前数为x时还需要几步到1,cnt[i]=cnt[sum[i]]+1,sum[i]为i在二进制下有多少个1,sum[i]=sum[i^lowbit(i)]+1,i^lowbit(i)就是i在二进制下去掉最右边的1,这个数一定先处理出来了,所以就可以递推,x在1000以内。

然后就可以进行数位DP,f[s][num][lim]表示当前处理到第i位,1的个数为num

那么最后cnt[num]+1==k就是一种,num就是枚举出的数转换一次得到的。

然后就WA了....

考虑k=0时,会输出0,但是容易发现1就是一个解,这是因为cnt是操作了一次之后,而1不需要操作,所以特判k=0即可。

k=1时,答案会多2,这是因为0和1的关系,所以把cnt[0]设成负数,然后特判k=1时答案-1即可。

 

技术图片
#include<bits/stdc++.h>
using namespace std;

const int mod=1000000007;
const int maxn=1005;
char s[maxn];
int k,len,num[maxn];
int sum[maxn],cnt[maxn];
int f[maxn][maxn][2];

void init(){
    sum[0]=0;
    cnt[1]=0;
    cnt[0]=-2;
    for(int i=1;i<=1000;i++) sum[i]=sum[i^(i&-i)]+1;
    for(int i=2;i<=1000;i++) cnt[i]=cnt[sum[i]]+1;
}

int dfs(int s,int c,bool lim){
    if(!s) return cnt[c]+1==k;
    if(f[s][c][lim]!=-1) return f[s][c][lim];
    int mx= lim ? num[s] : 1;
    int ret=0;
    for(int i=0;i<=mx;i++)
     ret=(ret+dfs(s-1,c+(i==1),lim&&i==mx))%mod;
    return f[s][c][lim]=ret;
}

int cx(){
    memset(f,-1,sizeof(f));
    return dfs(len,0,true);
}

int main(){
    init();
    scanf("%s%d",s,&k);
    if(!k){printf("1");return 0;}
    len=strlen(s);
    for(int i=1;i<=len;i++) num[i]=s[len-i]-0;
    int ans=cx();
    if(k==1) ans--;
    printf("%d",ans);
}
View Code

 

网上都是什么组合数,明明就有数位DP的标签(掩饰自己不会组合数)

 

CF914C Travelling Salesman and Special Numbers

标签:pre   ott   lap   amp   cli   bit   nbsp   ret   travel   

原文地址:https://www.cnblogs.com/sto324/p/11401958.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!