P7802 [COCI2015-2016#6] SAN 题解

对于一个数 \(\overline{s_1s_2s_3}\),进行一次变换后会变成 \(\overline{(s_1+s_3)(s_2+s_2)(s_3+s_1)}\),不考虑进位的话,很明显每变化一次就会变成一个回文串,且这个回文串最高位是 \(18\)

那我们就可以先预处理出来所有回文串的只用一次就能构成这个回文串的方案数。求这个的方案数即为每个位子上可能的数的乘积。

这个可以利用数位 dp 求解。

先枚举总位数,再枚举每一位,第一位不能为 \(0\),注意中间位数需要特殊处理,然后最后方案数相乘。

先离散化,然后处理不是在第一次就构成的情况,如果一个回文串能被回文串组成,那么方案数递归累加即可。

但还有个问题,如 \(5+5=10,10+1=11\),所以 \(5\) 会出现在以 \(5,10\) 开始的表中,但统计时实际上只统计了 \(10\)

这个好处理,对于 \(18\) 以内的数,求出其被 \(18\) 以下的数构成的方案数即可。

对第一位特殊处理,求出在 \(18\) 一下不包括 \(0\) 构成的方案数。

最后求前缀和,对于每一次询问,二分找到第一个不大于它的回文数,用前缀和求区间和即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
const int N=4e6+5;
int sum[N],cnt,power[15],p[20],q[20],lim[N];
struct node
{
	int name,data;
}b[N];
int cmp(node fi,node se)
{
	return fi.data<se.data;
}
int getnum(int x)
{
	int sum=0;
	while(x)
	{
		sum=sum*10+x%10;
		x/=10;
	}
	return sum;
}
int findsum(int x)
{
	int l=0,r=cnt;
	while(l<r)
	{
		int mid=(l+r+1)>>1;
		if(b[mid].data<=x)l=mid;
		else r=mid-1;
	}
	return l;
}
int clac(int x)
{
	int num=sum[findsum(x)];
	return num+x;
}
void dfs(int n,int x,int tot,int num)
{
	if(x>(n+1)/2)
	{
		lim[++cnt]=num;
		b[cnt]={cnt,tot};
		return;
	}
	for(int i=0;i<=18;i++)
	{
		if(x==1&&i==0)continue;
		if(n%2==1&&x==n/2+1&&i%2==1)continue;
		int tmp=p[i];
		if(x==1)tmp=q[i];
		if(tmp==0)continue;
		if(n%2==1&&x==n/2+1)dfs(n,x+1,tot+i*power[x-1],num*1);
		else dfs(n,x+1,tot+i*power[x-1]+i*power[n-x],num*tmp);
	}
}
void prepare()
{
	power[0]=1;
	for(int i=1;i<=12;i++)power[i]=power[i-1]*10;
	for(int i=0;i<=9;i++)for(int j=0;j<=9;j++)p[i+j]++,q[i+j]+=(i!=0);
	for(int i=1;i<=10;i++)dfs(i,1,0,1);
	sort(b+1,b+1+cnt,cmp);
	int bef=-1e18,cnp=0;
	for(int i=1;i<=cnt;i++)
	{
		if(b[i].data!=bef)b[cnp]={cnp,b[i-1].data},cnp++;
		bef=b[i].data;
		sum[cnp]+=lim[b[i].name];
	}
	for(int i=1;i<=cnp;i++)
	{
		int num=b[i].data+getnum(b[i].data);
		int tim=findsum(num);
		if(b[tim].data==num)sum[tim]+=sum[i];
		sum[i]+=sum[i-1];
		
	}
}
signed main()
{
	prepare();
	int q;
	scanf("%lld",&q);
	for(int i=1;i<=q;i++)
	{
		int l,r;
		scanf("%lld%lld",&l,&r);
		int ans=clac(r)-clac(l-1);
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2023-02-24 13:42  Gmt丶Fu9ture  阅读(29)  评论(0)    收藏  举报