題解 P7802 [COCI2015-2016#6] SAN
阿新 • • 發佈:2021-08-09
演算法分析:數位 dp
在講正解之前先說一下部分分。
注意到對於 \(50\%\) 的資料,\(1\le L,R\le10^6\),似乎可以暴力做出來。但由於我們不知道每個數可能出現的位置,直接開二維陣列模擬非常容易炸(事實證明它就炸了……),因此我們要換一種方式列舉。注意到每一個數字僅能轉變成為另一種數字,且每個數字至少出現一次(在第一列)。於是可以遞推得到 \(cnt_{rev(i)}+=cnt_i\) 然後字首和統計即可。
給出 \(50\%\) 的程式碼。可以獲得 \(80pts\) 的好成績。
namespace p50 { const int N=1e6; int cnt[N+5]; inline void init() { F(i,1,N)++cnt[i]; int p; F(i,1,N) { p=i+rev(i); if(p<=N)cnt[p]+=cnt[i]; } F(i,1,N)cnt[i]+=cnt[i-1]; } int main() { init(); n=read(); int l,r; F(i,1,n) { l=read(),r=read(); printf("%d\n",cnt[r]-cnt[l-1]); } } }
接下來講一下正解。
對於一個 \(x\),記 \(y=x+rev(x)\)。直接列舉 \(x\) 肯定超時,因此考慮列舉 \(y\)。在 \(y\) 有\(10\) 位時,先不計算進位,那麼 \(y\) 的前五位與後五位是對稱的。除最高位不為 \(0\)(那最低位也不為 \(0\)),其餘每一位可能是 \(0\sim 18\) 的一種。那麼總共有 \(18\times 19^4=2345778\) 種情況。是很小的,因此考慮用 dfs,先確定位數,再數位 dp 列舉 \(y\) 的每一位。
有了情況,接下來考慮統計次數。事實上,dfs 時已經算出了出現在第二列的次數,因為列舉的時候列舉的就是 \(x+rev(x)\)
程式碼在細節處略有不同,注意一下即可。
#include<bits/stdc++.h> #define ll long long #define reg register #define F(i,a,b) for(reg int i=(a);i<=(b);++i) using namespace std; bool beginning; inline ll read(); const int M=4e6+5,N=20; int n,cnt,p[N],q[N]; ll pw[N] {1},sum[M],a[M],tot[M],b[M]; inline ll rev(ll x) { ll ans=0; while(x)ans=ans*10+x%10,x/=10; return ans; } void dfs(int n,int x,ll s,ll t) { if(x==(n+1>>1)) { tot[++cnt]=t; b[cnt]=a[cnt]=s;//記錄可能的情況 return; } F(i,(x==0),18) { if(n==(x<<1|1) and (i&1))continue; ll tmp=x?p[i]:q[i]; if(n==(x<<1|1))tmp=1; if(!tmp)continue; if(n==(x<<1|1))dfs(n,x+1,s+pw[x]*i,t*tmp); else dfs(n,x+1,s+(pw[x]+pw[n-x-1])*i,t*tmp); } } inline void init() { F(i,1,15)pw[i]=pw[i-1]*10; F(i,0,9)F(j,0,9)++p[i+j],q[i+j]+=(i!=0); //出現某個和的不同組合情況數 F(i,1,10)dfs(i,0,0,1); //預處理 int m=cnt; sort(a+1,a+cnt+1); cnt=unique(a+1,a+cnt+1)-a-1; F(i,1,m) { int pos=lower_bound(a+1,a+cnt+1,b[i])-a; sum[pos]+=tot[i];//初值 } F(i,1,cnt) { ll t=a[i]+rev(a[i]); int pos=lower_bound(a+1,a+cnt+1,t)-a; if(pos!=cnt+1 and a[pos]==t)sum[pos]+=sum[i]; ++sum[i]; sum[i]+=sum[i-1]; } } inline ll cal(ll x) { if(!x)return 0; int pos=upper_bound(a+1,a+cnt+1,x)-a-1; return x+sum[pos]-pos; } bool ending; int main() { // system("color fc"); // printf("%.2lfMB\n",1.0*(&beginning-&ending)/1024/1024); init(); n=read(); ll l,r; F(i,1,n) { l=read(),r=read(); printf("%lld\n",cal(r)-cal(l-1)); } return 0; } inline ll read() { reg ll x=0; reg char c=getchar(); while(!isdigit(c))c=getchar(); while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar(); return x; }
歡迎交流討論,請點個贊哦~
本文來自部落格園,作者:楓のDark,轉載請註明原文連結:https://www.cnblogs.com/lbh2021/p/15116381.html