题解 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;
}
欢迎交流讨论,请点个赞哦~
本文来自博客园,作者:Maplisky,转载请注明原文链接:https://www.cnblogs.com/lbh2021/p/15116381.html