hihoCoder 1076 与链 (数位dp)

来源:互联网 发布:改良汉服淘宝店推荐 编辑:程序博客网 时间:2024/06/05 19:09

题意:

给定 n 和 k。计算有多少长度为 k 的数组 a1, a2, ..., ak,(0≤ai) 满足:a1 + a2 + ... + ak = n。
对于任意的 i = 0, ..., k - 1 有 ai AND ai + 1 = ai + 1。其中AND是与操作.

题解:

分析ai&ai+1=ai+1这个操作,我们会发现,ai+1必须比ai小或者等于ai才能满足,并且将其化成二进制会发现:

例如 ai+1=10 那么ai可以使:10 110 1110 11110....既然得到这个规律那么我们就可以根据数位dp按照位进行阶段划分,然后求解。dp[i][j]表示到第i为位置,和为j的个数。那么枚举这两状态外,还要枚举下一位1的个数,根据规律发现要满足条件时,某位有1都是往前靠的,比如说现在位i=2 那么这个为的1的排列只能是这样:

1 1 1 1、1 1 1 0、1 1 0 0、1 0 0 0、0 0 0 0.那么枚举1的个数其实就直接得到排列了,那么只要加上这位对应的和就ok了,不用考虑1的排列问题。

状态:dp[i+1][j+k]+=dp[i][j];

总的复杂度是O(n*k*17),但是想到这里就直接放弃不写了,因为这复杂度太高了,又想不到好的方法解决。。事实上复杂度没这么高!因为有一个地方可以优化!那么就是枚举1的个数时,我们令sum表示目前位置的总和,那么只要sum>n就break,这样时间优化非常多,至少10倍,于是就可以过了!

#include<iostream>#include<math.h>#include<stdio.h>#include<algorithm>#include<string.h>#include<vector>#include<queue>#include<map>#include<set>using namespace std;#define B(x) (1<<(x))typedef long long ll;void cmax(int& a,int b){ if(b>a)a=b; }void cmin(int& a,int b){ if(b<a)a=b; }const int oo=0x3f3f3f3f;const int MOD=1000000009;const int maxn=10005;ll dp[20][maxn];int main(){    //freopen("E:\\read.txt","r",stdin);    int k,n,T;    scanf("%d",&T);    while(T--){        scanf("%d %d",&k,&n);        memset(dp,0,sizeof dp);        dp[0][0]=1;        for(int i=0;i<17;i++){            for(int j=0;j<=n;j++){                if(dp[i][j]==0)continue;                for(int l=0;l<=k;l++){                    ll sum=j+(l<<i);                    if(sum>n)break;                    dp[i+1][sum]=(dp[i+1][sum]+dp[i][j])%MOD;                }            }        }        cout<<dp[17][n]<<endl;    }return 0;}



0 0
原创粉丝点击