题解 CF288E 【Polo the Penguin and Lucky Numbers】

· · 题解

卡了两天,调出来了……

这题明显一眼数位DP。但是,同一般数位DP不同的是,这里的DP要在边界数(即除了首位其它位都是 7 的数)处格外小心——因为,其它数假设我们在前面加上一个数(例如 4),应用乘法分配律,我们会发现:

(\overline{4x})\times(\overline{4y})=(4\times10^i)^2+4\times10^i(x+y)+xy

其中上划线表示将两数拼接。

但是,独独在边界数处,上式会失效(因为首位进了一位)。

我前几个思路设计的DP状态均包含了边界数,但是我最终发现包含边界数的状态非常难以转移,所以就有了下文的DP状态:

$sum_{i,j}$ 表示长度为 $i$,首位为 $j$ 的数的和(**包含边界数**); $ans_{i,j}$ 表示长度为 $i$,首位为 $j$ 的数的答案(**不含边界数**); 下面考虑转移—— $$num_{i,j}=num_{i-1,4}+num_{i-1,7}+1$$ 因为在前面填上一个数后,原本的边界数 $4777\dots$ 便不再是边界数了,故要加一。同时我们可以发现必有 $num_{i,4}=num_{i,7}$,但是为了区别我们还是把它们分开。 $$sum_{i,j}=\overline{j000\dots}\times(num_{i,j}+1)+sum_{i-1,4}+sum_{i-1,7}$$ 这里的 $+1$ 是因为 $num$ 不包含边界,但 $sum$ 包含边界。 下面就是重头戏了。 我们考虑到上式 $$(\overline{4x})\times(\overline{4y})=(4\times10^i)^2+4\times10^i(x+y)+xy$$ 于是求和便得到 $$ans_{i,j}=(\overline{j000\dots})^2\times num_{i.j}+\overline{j000\dots}\times\Big(2(sum_{i-1,4}+sum_{i-1,7})-\overline{777\dots}-\overline{444\dots}\Big)+(ans_{i-1,4}+ans_{i-1,7}+\overline{4777\dots}\times\overline{7444\dots})$$ 其中加上或减去的一些都是边界数。 在预处理出三个数组之后,就可以类似地DP出 $l$ 和 $r$ 处的前缀和,并相减得到答案。具体过程和此类似,不再赘述。详情可以见代码。 代码: ```cpp #include<bits/stdc++.h> using namespace std; const int mod=1e9+7; const int inv10=700000005; int sum[100100][2],num[100100][2],ans[100100][2],n,res; //sum[i][j]:the sum of numbers whose length is i and begin with number j. //num[i][j]:the amount of so [except the boundary value,which is x7777...] //ans[i][j]:the answer of so [except the boundary value,which is (x7777...)*(y4444...)] char s[100100]; int func(){ int _74=7,_47=4,_10=10,_4=4; for(int i=1;i<n;i++)_74=(10ll*_74+4)%mod,_47=(10ll*_47+7)%mod,_10=10ll*_10%mod,_4=(10ll*_4+4)%mod; int pref=0,ret=0; for(int i=0;i<n;i++){ if(s[i]=='7'){ int now=1ll*pref*_10%mod; int tmp=(2ll*sum[n-i-1][0]+(mod-_4)+(mod-_47))%mod; (ret+=(0ll+ans[n-i-1][0]+(1ll*now*now%mod)*num[n-i-1][0]%mod+1ll*now*tmp%mod+1ll*(now+_47)*(now+_74)%mod)%mod)%=mod; } pref=(10ll*pref+s[i]-'0')%mod; _74=1ll*(_74+mod-4)*inv10%mod,_47=1ll*(_47+mod-7)*inv10%mod,_10=1ll*_10*inv10%mod,_4=1ll*(_4+mod-4)*inv10%mod; } return ret; } int main(){ scanf("%s",s),n=strlen(s); sum[0][0]=4,sum[0][1]=7; int _74=7,_47=4,_10=10,_7=7,_4=4;//74444...;47777...;1000...;7777...;4444.... for(int i=1;i<n;i++){ int __4=4ll*_10%mod,__7=7ll*_10%mod;//4000...;7000...; int tmp=(2ll*(sum[i-1][0]+sum[i-1][1])+(mod-_7)+(mod-_4))%mod;//the sum of the previous stage,except the two boundary values num[i][0]=(num[i-1][0]+num[i-1][1]+1)%mod;//+1 because after adding a number in the beginning, one previous boundary value is no longer hold. num[i][1]=(num[i-1][0]+num[i-1][1]+1)%mod;//num[i][0] always equals to num[i][1]; however we keep them both so as to distinguish them. ans[i][0]=(0ll+(1ll*_74*_47%mod+ans[i-1][0]+ans[i-1][1])%mod+(1ll*__4*__4%mod)*num[i][0]%mod+1ll*__4*tmp%mod)%mod; ans[i][1]=(0ll+(1ll*_74*_47%mod+ans[i-1][0]+ans[i-1][1])%mod+(1ll*__7*__7%mod)*num[i][1]%mod+1ll*__7*tmp%mod)%mod; //consider using the equation (k+x)(k+y)=k^2+k(x+y)+xy,and you'll find why the transmission is like this.[remember to consider the no-longer boundary] sum[i][0]=(1ll*__4*(num[i][0]+1)%mod+sum[i-1][0]+sum[i-1][1])%mod;//still there's +1. sum[i][1]=(1ll*__7*(num[i][1]+1)%mod+sum[i-1][0]+sum[i-1][1])%mod; _74=(10ll*_74+4)%mod,_47=(10ll*_47+7)%mod,_7=(10ll*_7+7)%mod,_4=(10ll*_4+4)%mod,_10=10ll*_10%mod; } res=(mod-func())%mod; scanf("%s",s); (res+=func())%=mod; printf("%d\n",res); return 0; } ```