题目描述:
算法标签:数位DP
思路:
以下叫x=x-1为操作一,x=x-2i为操作2.
对于一个数,我们可以把它分成两部分,第一部分只需要用操作2,第二部分需用用若干个操作2,是序列变成100...0000再使用操作1,再把该为0的部分使用操作2。
分成两部分分别从头和尾dp,最后在合起来。
以下代码:
#include<bits/stdc++.h> #define il inline using namespace std; const int N=502,p=1e9+7;char s[N]; int n,f[N][2],g[N][2][2],fw[N][2],gw[N][2][2],a[N],ans; il int mu(int x,int y){if(x+y>=p)return x+y-p;return x+y;} int main() { scanf(" %s",s+1);n=strlen(s+1);for(int i=1;i<=n;i++)a[i]=s[i]-'0'; gw[n+1][0][0]=1; for(int i=n;i>1;i--)for(int j=0;j<2;j++)for(int z=0;z<2;z++){ if(!gw[i+1][j][z])continue; for(int ax=0;ax<2;ax++)for(int bx=0;bx<2;bx++){ int jj,zz=(z|(ax==1&&bx==0)),val=(ax==0)+(bx==1); if(a[i]==bx)jj=j;else if(a[i]<bx)jj=1;else jj=0; if(ax==0&&bx==1)zz=0; gw[i][jj][zz]=mu(gw[i][jj][zz],gw[i+1][j][z]); g[i][jj][zz]=mu(g[i][jj][zz],mu(g[i+1][j][z],1ll*val*gw[i+1][j][z]%p)); } } fw[0][1]=1; for(int i=1;i<=n;i++)for(int j=0;j<2;j++){ if(!fw[i-1][j])continue; for(int ax=0;ax<2;ax++)for(int bx=0;bx<2;bx++){ if(ax==1&&bx==0)continue;if(j==1&&a[i]<bx)continue; int jj=(j==1&&a[i]==bx),val=bx-ax; fw[i][jj]=mu(fw[i][jj],fw[i-1][j]); f[i][jj]=mu(f[i][jj],mu(f[i-1][j],1ll*val*fw[i-1][j]%p)); if(val&&i!=n){ ans=mu(ans,mu(1ll*fw[i-1][j]*g[i+1][0][1]%p,mu(1ll*gw[i+1][0][1]*fw[i-1][j]%p,1ll*f[i-1][j]*gw[i+1][0][1]%p))); if(!jj)ans=mu(ans,mu(1ll*fw[i-1][j]*g[i+1][1][1]%p,mu(1ll*gw[i+1][1][1]*f[i-1][j]%p,1ll*fw[i-1][j]*gw[i+1][1][1]%p))); } } } ans=mu(ans,mu(f[n][0],f[n][1])); printf("%d ",ans); return 0; }