删除数字

来源:互联网 发布:网络机房装修标准 编辑:程序博客网 时间:2024/06/16 16:46

题目描述

给定两个数A和N,形成一个长度为N+1的序列,(A,A+1,A+2,…,A+N-1,A+N)。
每次操作可以把第i个数上的第x位数字删除,形成一个新的数字。
求有多少种方案,是的最后的数字是单调不递减的。
两种方案是认为不同,如果第i个数的第x位在一个方案中被删除,在另一个方案中,没有被删除。

输入

输入两个整数A和N

输出

输出方案数,对1e9+7 求余。

题目分析:
关键字:DP,二进制枚举
其实DP是比较显然的,现在的决策就是对于目前这个数,删掉它的哪几个数字。用二进制枚举,把剩下的数字取出来变回去,从前一次的状态中把比它小的状态全部转移过来即可,主要还是转移的这部分,因为直接用数的大小作为下标的话空间时间都不够用,所以可以离散了值,从排序完的序列中找到对应的最后一个位置,把值加过来,在加上前缀和维护值就不会复杂度太高了。

直接看代码:

#include<bits/stdc++.h>using namespace std;#define LL long long#define MOD 1000000007#define fin(x) freopen(x,"r",stdin)#define fout(x) freopen(x,"w",stdout)LL a;int n;void chkmax(int &a,int b) {    if(a<b) a=b;}void chkmin(int &a,int b) {    if(a>b) a=b;}void add(int &a,int b) {    b%=MOD;    a+=b;    a%=MOD;}void add(int &a,LL b){    b%=MOD;    a+=b;    a%=MOD;}int tmp[22];LL A[305];struct NODE{    LL res,dp;};bool cmp(NODE A,NODE B) {    return A.res<B.res;}struct node{    int cnt;    int sum[20005];    NODE ok[20005];    int Find(LL x){        int L=1,R=cnt,Res=0;        while(L<=R) {            int mid=(L+R)>>1;            if(ok[mid].res<=x) L=mid+1,Res=mid;            else R=mid-1;        }        return Res;    }    void init() {        for(int i=1;i<=cnt;i++){            sum[i]=0;            add(sum[i],ok[i].dp+sum[i-1]);        }    }} DP[2]; //ok存的是值int len,cur;//记录上一层方案ok的个数int f(LL x) {    int cnt=0;    while(x) {        tmp[cnt++]=x%10;//tmp从右到左        x/=10;    }    return cnt;}void Init(){    for(int i=0;i<=n;i++) A[i]=a+i;}void solve() {    Init();    int m0=f(A[0]);    DP[cur].cnt=0;    for(int j=1; j<(1<<m0); j++) {        LL num=0;        for(int k=m0-1; k>=0; k--) if( (1<<k)&j ) num=num*10+tmp[k]; //从左到右        DP[cur].ok[++DP[cur].cnt].res=1LL*num;        DP[cur].ok[DP[cur].cnt].dp=1;    }    sort(DP[cur].ok+1,DP[cur].ok+1+DP[cur].cnt,cmp);    DP[cur].init();    for(int i=1; i<=n; i++) {        int m=f(A[i]);        DP[1-cur].cnt=0;        for(int j=1; j<(1<<m); j++) {            LL num=0;            for(int k=m-1; k>=0; k--) if( (1<<k)&j ) num=num*10+tmp[k]; //从左到右            int pos=DP[cur].Find(num);            DP[1-cur].ok[++DP[1-cur].cnt].dp=DP[cur].sum[pos];            DP[1-cur].ok[DP[1-cur].cnt].res=num;        }        cur=1-cur;        sort(DP[cur].ok+1,DP[cur].ok+1+DP[cur].cnt,cmp);        DP[cur].init();//统计前缀和    }    printf("%d\n",DP[cur].sum[DP[cur].cnt]%MOD);}int main() {    cin>>a>>n;    solve();    return 0;}
原创粉丝点击