• T2988 删除数字【状压Dp+前缀和优化】


    Online Judge:从Topcoder搬过来,具体哪一题不清楚

    Label:状压Dp+前缀和优化

    题目描述

    给定两个数A和N,形成一个长度为N+1的序列,(A,A+1,A+2,...,A+N-1,A+N)。

    每次操作可以把第i个数上的第x位数字删除,形成一个新的数字。

    每个数字可以操作任意次,但不可以全部删完。

    求有多少种方案,使得最后的序列中数字是单调不递减的。

    两种方案是认为不同,如果第i个数的第x位在一个方案中被删除,在另一个方案中,没有被删除。

    Tip:注意一个数字不能所有位全部删光,至少得有一位还保存着。另外,假如数字(100)删成(00)(0),他们表示的值都是0,但两种方案视为不同。

    输入

    输入两个整数A和N

    输出

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

    样例

    Input#1

    10 2
    

    Output#1

    15
    

    Input#2

    111 11
    

    Output#2

    184432
    

    Hint

    对于30%的数据,A的范围[(1,100)],N的范围[(1,10)];
    对于70%的数据,A的范围[(1,10^9)];
    对于100%的数据,A的范围[(1,10^{12})],N的范围[(1,100)];

    题解

    题面给的是一个逐渐上升的连续整数序列,一开始以为有什么性质,但最后做法好像都是状压dp,所以就看成一个普通序列来做就行了,可能TC的题输入太多不方便??。

    30pts

    对于30%数据,由于n的范围不大,数字位数也不多,可以直接暴力枚举每个数删除哪些位。

    先预处理一个数组(sum[i][sta]),表示第i位的状态为(sta)时的值(二进制,0表示删除,1表示保留)。例如第i个数字为123时,(sum[i][3(11)]=23)(sum[i][7(111)]=123)

    枚举用dfs实现。总的时间复杂度为(O({2^{3}}^n))。加上一些剪枝记忆化,可以跑过。

    70pts

    把dfs改成动态规划转移即可。倒推。

    数字位数最多为9,用二进制位表示每一种删除方案。

    定义状态(dp[i][sta])表示现在已经决策完第(i..n+1)个数字了,且第i个数字的状态为(sta)(二进制位,0表示删除,1表示保留)。

    转移的话只要按上面的dfs改一改就好了。大致思路是,仍然先预处理上面的sum数组,然后枚举第i位,枚举这个数字的状态j,枚举上一个数字的状态k,如果(sum[i][j]<=sum[i+1][k])(dp[i][j]+=dp[i+1][k])

    时间复杂度为(O(ncdot 2^9 cdot 2^9))。如果当数字上限增加到1e12时就跑不动了。

    100pts

    根据条件(sum[i][j]<=sum[i+1][k])很容易想到用前缀和/后缀和去优化,免去枚举第三层k。

    但由于(sum)并非递增的,所以还得排个序,最后求和时二分一下第一个大于等于(sum[i][j])的位置k,使得(sum[i][j]<=sum[i+1][k])

    注意细节。

    #include<bits/stdc++.h>
    #define ll long long
    #define mod 1000000007
    using namespace std;
    const int N=103;
    ll A,a[N],sum[N][4100],dp[N][4100],tot[N][4100];
    int n,num[N][14],len[N];
    ll pw[14];
    
    inline void Do(ll &x,ll y){
    	x+=y;
    	if(x>=mod)x-=mod;
    }
    int main(){
    	scanf("%lld%d",&A,&n);
    	register int i,j,k;
    	
    	pw[0]=1;
    	for(i=1;i<=12;++i)pw[i]=pw[i-1]*10;
    	
    	for(i=0;i<=n;++i)a[i+1]=A+1ll*i;
    		
    	for(i=1;i<=n+1;++i){
    		ll o=a[i];
    		while(o){
    			num[i][len[i]++]=o%10;
    			o/=10;
    		}
    	}
    	for(i=1;i<=n+1;++i){
    		for(j=0;j<(1<<len[i]);++j){
    			int cnt=0;
    			for(k=0;k<len[i];++k)if((1<<k)&j){
    				sum[i][j]+=1ll*pw[cnt]*num[i][k];
    				++cnt;
    			}
    		}		
    	}
    	
    	for(int i=1;i<=n+1;i++)sort(sum[i]+1,sum[i]+(1<<len[i]));
    	
    	for(i=1;i<(1<<len[n+1]);++i)dp[n+1][i]=1;
    	for(j=(1<<len[n+1])-1;j>=1;j--)tot[n+1][j]=(tot[n+1][j]+dp[n+1][j]+tot[n+1][j+1])%mod;
    		
    	for(i=n;i>=1;i--){//当前轮
    		for(j=1;j<(1<<len[i]);++j){
    			ll nowj=sum[i][j];
    			int o=lower_bound(sum[i+1]+1,sum[i+1]+(1<<len[i+1]),nowj)-sum[i+1];
    		//	cout<<"i:"<<i<<" nowj:"<<nowj<<" o:"<<o<<" sumk:"<<sum[i+1][o]<<endl;
    		//	cout<<"now add:"<<tot[i+1][o]<<endl;	
    			Do(dp[i][j],tot[i+1][o]);
    		} 
    		for(j=(1<<len[i])-1;j>=1;j--)tot[i][j]=(tot[i][j]+(tot[i][j+1]+dp[i][j])%mod)%mod;
    		tot[i][0]=tot[i][1];
    	}
    		
    	ll ans=0;
    	for(i=1;i<(1<<len[1]);++i)if(dp[1][i])Do(ans,dp[1][i]);
    	printf("%lld
    ",(ans+mod)%mod);
    }
    

    update:内存的优化(dp的第一维可以滚动,或者直接去掉改一下转移顺序)。但是由于n比较小没什么必要。

  • 相关阅读:
    一致性hash算法
    Mapreduce部署与第三方依赖包管理
    windows10下使用spark-2.3.0-bin-without-hadoop相关问题
    VirtualBox mount报错:/sbin/mount.vboxsf: mounting failed with the error: No such device
    如何重命名MongoDB中的replica set
    MongoDB开发环境Replica Set单机部署流程
    MongoDB修改Replica Set的服务器名和端口
    Axios -- Ajax请求
    element中的table表格显示时间格式问题
    java 常用工具类
  • 原文地址:https://www.cnblogs.com/Tieechal/p/11584236.html
Copyright © 2020-2023  润新知