1. 程式人生 > 其它 >題解 P7802 [COCI2015-2016#6] SAN

題解 P7802 [COCI2015-2016#6] SAN

題目傳送門

演算法分析:數位 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)\)

。對於更後面的列,由於後面每個數都是由前面的數轉移來的,那麼可以列舉每一個滿足要求的數。由於所有的可能的數都已經處理出來了,就可以用一個數組 \(sum\) 統計一個數出現的次數。和上面類似的,\(sum_{x+rev(x)}+=sum_x-1\)。因為第二行已經統計過了,所以減一。對 \(sum\) 做字首和即可做到快速查詢。

程式碼在細節處略有不同,注意一下即可。

#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;
}

AC

歡迎交流討論,請點個贊哦~

本文來自部落格園,作者:楓のDark,轉載請註明原文連結:https://www.cnblogs.com/lbh2021/p/15116381.html