zoukankan      html  css  js  c++  java
  • 【BZOJ3326】数数(SCOI2013)-数位DP

    测试地址:数数
    题目大意: 给定L,RL,R两个10510^5位内的B(105)B(le 10^5)进制数,LRLle R,对区间[L,R][L,R]内的所有数xx,累加xx中所有子串表示的数字的和(如123123,应该累加123+12+23+1+2+3123+12+23+1+2+3到答案中,注意不应该包含前导零),求最终答案(用1010进制表示)。
    做法: 本题需要用到数位DP。
    神题。虽然一眼能看出数位DP,但具体的转移式子还是想错了好多回,这次终于写对了。
    首先我们来看,对于一个lenlen位的数xx,在它末尾加一位数ss,会对这个数的所有子串表示的数字和有什么影响(以下简写成sum(x)sum(x))。令suf(x)suf(x)表示数xx所有后缀表示的数字和,那么有:
    suf(xnew)=suf(xlast)B+s(len+1)suf(x_{new})=suf(x_{last})cdot B+scdot (len+1)
    sum(xnew)=sum(xlast)+suf(xnew)sum(x_{new})=sum(x_{last})+suf(x_{new})
    根据这个为基础,我们就能思考数位DP的转移了。
    根据套路,首先把问题转化为:用小于等于RR的所有数的贡献,减去小于等于L1L-1的所有数的贡献,于是现在我们考虑求小于等于某个数TT时的贡献。
    从高位向低位枚举,令Sum(i,0/1)Sum(i,0/1)表示前ii位中,不卡/卡上界的所有数的sum(x)sum(x)的和,Suf(i,0/1)Suf(i,0/1)表示前ii位中,不卡/卡上界的所有数的suf(x)suf(x)的和。先分析具体的转移过程:
    i1i-1位的数不卡上界时,第ii位可以填00 ~ B1B-1内所有的数,并且新的数都不卡上界;
    i1i-1位的数卡上界时,第ii位可以填00 ~ T[i]T[i]。当填00 ~ T[i]1T[i]-1时,新的数不卡上界,当填T[i]T[i]时新的数卡上界。
    于是我们先考虑SufSuf的转移。首先,卡上界的情况应该很好转移了,实际上就是求上界的sufsuf值。主要是不卡上界的情况比较复杂。
    首先考虑不卡上界转移到不卡上界的情况。根据上面的转移式子suf(xnew)=suf(xlast)B+s(len+1)suf(x_{new})=suf(x_{last})cdot B+scdot (len+1),我们这样考虑:首先枚举ss00B1B-1,对于每一个ss,再枚举可转移的xlastx_{last},把贡献累加起来。于是一个ss对整个SufSuf的贡献是:Bsuf(xlast)+s(len(xlast)+1)Bcdot sum suf(x_{last})+scdot sum (len(x_{last})+1),那么对于所有ss,对SufSuf的贡献就是:BBsuf(xlast)+B(B1)2(len(xlast)+1)Bcdot Bcdot sum suf(x_{last})+frac{B(B-1)}{2}cdot sum (len(x_{last})+1)。其中suf(xlast)sum suf(x_{last})就是Suf(i1,0)Suf(i-1,0),而(len(xlast)+1)sum (len(x_{last})+1)需要斟酌一下。这个和式是在求,对于所有可转移的xlastx_{last}(包括00),累加它们的位数+1+1(把00的位数看做00)。观察规律,我们发现:1111个,22B1B-1个,33(B1)B(B-1)cdot B个,44(B1)B2(B-1)cdot B^2个…i1i-1(B1)Bi3(B-1)cdot B^{i-3}个,iinumBi2num-B^{i-2}个,numnum表示TT的前i1i-1位组成的前缀。那么前面的有规律的部分可以递推维护,而numnum显然也可以递推维护,所以我们就可以每次O(1)O(1)地进行这个转移了。
    接下来考虑卡上界转移到不卡上界的情况。这种情况下,能转移到不卡上界的情况,ss必须是00 ~ T[i]1T[i]-1,而且因为这种情况中可转移的xlastx_{last}只有一个,而且len(xlast)len(x_{last})就是i1i-1,因此就比上面的情况简单很多了,总贡献应该为Suf(i1,1)B+T[i](T[i]1)2iSuf(i-1,1)cdot B+frac{T[i](T[i]-1)}{2}cdot i
    那么SufSuf的转移讨论完了,接下来讨论SumSum的转移。Sum(i,1)Sum(i,1)就是求上界的sumsum,而Sum(i,0)Sum(i,0)也利用上面的思考方式,先考虑从不卡上界转移的情况,因为有BB种转移,所以Sum(i1,0)Sum(i-1,0)就产生了BB次的贡献。再考虑从卡上界转移的情况,因为有T[i]T[i]种转移,所以Sum(i1,1)Sum(i-1,1)就产生了T[i]T[i]次的贡献。再加上所有不卡上界数的后缀产生的贡献,即Suf(i,0)Suf(i,0),就可以计算出Sum(i,0)Sum(i,0)了。
    至此,经过漫长的讨论,我们得到了一个O(n)O(n)的数位DP,完美地解决了这一道题。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll mod=20130427;
    int n;
    ll s[100010],B,sum[100010][2],suf[100010][2];
    ll pw[100010],num[100010],sumnum[100010]={0};
    
    ll solve()
    {
    	sum[0][0]=sum[0][1]=suf[0][0]=suf[0][1]=num[0]=0;
    	for(int i=1;i<=n;i++)
    	{
    		if (i>2) sumnum[i]=(sumnum[i-1]+(ll)(i-1)*(B-1ll)%mod*pw[i-3]%mod)%mod;
    		num[i]=(num[i-1]*B%mod+s[i])%mod;
    		ll tmp=0;
    		if (i>1) tmp=(sumnum[i]+1ll+(num[i-1]-pw[i-2]+mod)%mod*(ll)i%mod)%mod;
    		suf[i][1]=(suf[i-1][1]*B%mod+s[i]*(ll)i%mod)%mod;
    		suf[i][0]=(suf[i-1][0]*B%mod*B%mod+B*(B-1ll)/2ll%mod*tmp%mod)%mod;
    		suf[i][0]=(suf[i][0]+suf[i-1][1]*B%mod*s[i]%mod+s[i]*(s[i]-1ll)/2ll%mod*(ll)i%mod)%mod;
    		sum[i][1]=(sum[i-1][1]+suf[i][1])%mod;
    		sum[i][0]=(sum[i-1][0]*B%mod+suf[i][0])%mod;
    		sum[i][0]=(sum[i][0]+sum[i-1][1]*s[i]%mod)%mod;
    	}
    	return (sum[n][0]+sum[n][1])%mod;
    }
    
    int main()
    {
    	ll ans;
    	
    	scanf("%lld",&B);
    	scanf("%d",&n);
    	pw[0]=1;
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%lld",&s[i]);
    		pw[i]=pw[i-1]*B%mod;
    	}
    	ans=(mod-solve()+sum[n][1])%mod;
    	
    	scanf("%d",&n);
    	pw[0]=1;
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%lld",&s[i]);
    		pw[i]=pw[i-1]*B%mod;
    	}
    	ans=(ans+solve())%mod;
    	
    	printf("%lld",ans);
    	
    	return 0;
    }
    
  • 相关阅读:
    SpringJMS解析2-JmsTemplate
    SpringJMS解析1-使用示例
    SpringHttpInvoker解析3-客户端实现
    算法笔记_084:蓝桥杯练习 11-1实现strcmp函数(Java)
    算法笔记_083:蓝桥杯练习 合并石子(Java)
    算法笔记_082:蓝桥杯练习 12-1三角形(Java)
    算法笔记_081:蓝桥杯练习 算法提高 矩阵乘法(Java)
    算法笔记_080:蓝桥杯练习 队列操作(Java)
    算法笔记_079:蓝桥杯练习 区间k大数查询(Java)
    算法笔记_078:蓝桥杯练习 最大最小公倍数(Java)
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793245.html
Copyright © 2011-2022 走看看