Beautiful numbers[數位$dp$+狀壓]
阿新 • • 發佈:2020-11-24
Beautiful numbers[數位\(dp\)+狀壓]
將0~9數字否存在狀壓到s
再將數字膜上\(1,2,3,4,5,6,7,8,9,\)和20位數放入\(dp\)。
這樣空間會有\(9!\times2^{10}\times20\)很明顯會超時。
再往每個膜數上優化。
首先如果一個數x % 8為\(0,2,4,6\)那麼x%2為\(0\)(即\(2\)的倍數)。
膜上2這個可以去除。
一個數x%9為0,3,6,那麼x%3為\(0\)(即\(3\)的倍數)。
膜上3這個可以去除。
一個數x%8為\(0,4\)那麼x%4為\(0\)(即\(4\)的倍數)。
膜上4這個可以去除。
如果一個數x % 8為\(0,2,4,6\)並且x%9為\(0,3,6\)(即既是2的倍數,又是3的倍數),那麼x%6為\(0\)。
膜上6這個可以去除。
即可以將膜上\(1,2,3,4,5,6,7,8,9,\)優化為膜上\(5,7,8,9\)
且只需要存上是否存在2~9的數字(0和1不需要判斷)。
這樣空間會有\(5\times7\times8\times9\times2^8\times20=12,902,400\)勉強能開下。
#include <iostream> #include <cstring> using namespace std; typedef long long ll; ll t,l,r,a[20]; ll dp[20][5][7][8][9][307]; ll lin; ll ksm(ll x,ll p){ ll res=1; while(p){ if(p%2==1) res=res*x; p/=2; x=x*x; } return res; } ll dfs(ll p,ll wu,ll qi,ll ba,ll jiu,ll s,ll lim){ if(p==0){ ll ok=1; for(ll i=0;i<=7;i++){ if(s&(1<<i)){ if(i==0){ if(!(ba==0||ba==2||ba==4||ba==6)){ ok=0; } } if(i==1){ if(!(jiu==0||jiu==3||jiu==6)){ ok=0; } } if(i==2){ if(!(ba==0||ba==4)){ ok=0; } } if(i==3){ if(wu!=0){ ok=0; } } if(i==4){ if(!(ba==0||ba==2||ba==4||ba==6)){ ok=0; } if(!(jiu==0||jiu==3||jiu==6)){ ok=0; } } if(i==5){ if(qi!=0){ ok=0; } } if(i==6){ if(ba!=0){ ok=0; } } if(i==7){ if(jiu!=0){ ok=0; } } } } return ok; } if(lim==0&&dp[p][wu][qi][ba][jiu][s]!=-1){ return dp[p][wu][qi][ba][jiu][s]; } ll up=lim? a[p]:9; ll ans=0; for(ll i=0;i<=up;i++){ if(i>=2){ ans+=dfs(p-1,(wu+ksm(10,p-1)*i)%5,(qi+ksm(10,p-1)*i)%7,(ba+ksm(10,p-1)*i)%8 ,(jiu+ksm(10,p-1)*i)%9,s|(1<<(i-2)),lim&&i==up); } else{ ans+=dfs(p-1,(wu+ksm(10,p-1)*i)%5,(qi+ksm(10,p-1)*i)%7,(ba+ksm(10,p-1)*i)%8 ,(jiu+ksm(10,p-1)*i)%9,s,lim&&i==up); } } if(lim==0)return dp[p][wu][qi][ba][jiu][s]=ans; else return ans; } ll solve(ll num){ ll tot=0; while(num){ a[++tot]=num%10; num/=10; } return dfs(tot,0,0,0,0,0,1); } int main(){ scanf("%lld",&t); memset(dp,-1,sizeof(dp)); while(t--){ scanf("%lld %lld",&l,&r); printf("%lld\n",solve(r)-solve(l-1)); } }