SAN

link

一道挺有价值的题,标的是数位DP但感觉不像呢,它感觉更像是一个递推和差分,DP倒真的说不上(毕竟它没有记忆化)。

首先第一列出现的元素是容易求的,但第二列似乎是麻烦的。但考虑一下发现,容易想到假如不考虑进位的话那么第二列的所有数应该都是回文数,而既然都是回文数了那么它的取值集合的大小就会大大减小,由于每一位都是在\([0,18]\)中的,回文数最多10位(也就是有5位是可选择的),那么取值应该是在\(18^5\) 级别的,甚至可以枚举。用dfs可以直接枚举出所有在第二列中出现的元素,然后这些元素的出现次数也是可以通过排列组合求得的。然后就考虑第三列第四列以及后面的东西。由于这个集合中的数那也是自然数(这不废话),它们经过变换后的数应该也在集合内,这就构成了一个树形结构,求数量就是求子树大小,递归即可。递归之后求前缀和就可以啦。

代码还好,不算太烧脑。

#include<bits/stdc++.h>
//#define feyn
#define int long long
const int M=8;
const int N=5000010;
const int S=1e11;
using namespace std;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}
inline int min(int s1,int s2){
	return s1<s2?s1:s2;
}

inline int p(int wh){
	int cnt=0,c[12]={0};
	while(wh)c[++cnt]=wh%10,wh/=10;
	for(int i=1;i<=cnt;i++)wh=wh*10+c[i];
	return wh;
}

struct node{
	int data,an;
}s[N];
inline bool operator <(node s1,node s2){
	return s1.data<s2.data;
}
int cnt;

int a[M],sa[20],sb[20];
inline void dfs(int wh){
	if(wh>5)return;
	if(wh){
		//统计单数位
		if(a[wh]%2==0){
			int data=0,an=1;
			for(int i=1;i<wh;i++){
				data=data*10+a[i];
				if(i==1)an=an*sb[a[i]];
				else an=an*sa[a[i]];
			}
			for(int i=wh;i;i--)data=data*10+a[i];
			s[++cnt]=(node){data,an};
			//if(wh==1)printf("%lld %lld\n",data,an);
		}
		//统计偶数位
		int data=0,an=1;
		for(int i=1;i<=wh;i++){
			data=data*10+a[i];
			if(i==1)an=an*sb[a[i]];
			else an=an*sa[a[i]];
		}
		for(int i=wh;i;i--)data=data*10+a[i];
		s[++cnt]=(node){data,an};
		//if(wh==2)printf("%lld %lld\n",data,an);
	}
	for(int i=(wh==0)?1:0;i<=18;i++){
		a[wh+1]=i;dfs(wh+1);
	}
}

inline int find(int wh){
	//find the first one.data<=wh
	int l=0,r=cnt,mid;
	while(l<r)s[(mid=l+r+1>>1)].data<=wh?l=mid:r=mid-1;
	//if(wh<=200)printf("find %lld %lld\n",wh,s[l].data);
	return l;
}
inline int work(int wh){
	return wh==0?0:wh+s[find(wh)].an;
}

signed main(){
	
	#ifdef feyn
	freopen("in.txt","r",stdin);
	#endif
	
	for(int i=0;i<10;i++)
		for(int j=0;j<=18;j++)
			if(j-i>=0&&j-i<=9){sa[j]++;if(i)sb[j]++;}
	dfs(0);sort(s+1,s+cnt+1);
	int cnt_=0;
	for(int i=1;i<=cnt;i++){
		if(s[i].data==s[i+1].data){
			s[i+1].an+=s[i].an;
			s[i].data=0;
		}
		if(s[i].data)s[++cnt_]=s[i];
	}
	//for(int i=1;i<=50;i++)printf("a %lld %lld\n",s[i].data,s[i].an);
	//printf("working\n");
	cnt=cnt_;
	for(int i=1;i<=cnt;i++){
		int data=s[i].data+p(s[i].data);
		if(data>S)continue;
		s[find(s[i].data+p(s[i].data))].an+=s[i].an;
	}
	for(int i=1;i<=cnt;i++){
		s[i].an+=s[i-1].an;
	}
	
	//for(int i=1;i<=50;i++)printf("%lld %lld\n",s[i].data,s[i].an);
	
	int m,l,r;read(m);
	while(m--){
		read(l);read(r);
		printf("%lld\n",work(r)-work(l-1));
	}
	
	
	return 0;
}
posted @ 2022-07-24 21:36  Feyn618  阅读(53)  评论(0编辑  收藏  举报