[CSP校内集训]reverse(数位DP)

题意

给一个范围[L,R],求满足(Lleq n leq R)(Lleq rev(n) leq R)(n)的个数,其中(rev(n))表示将(n)翻转((123->321)),多组询问((L,Rleq 2^64-1))

思路

长这样的计数问题,没什么悬念考虑数位DP

只设(f_{i,lim})表示处理到第(i)位是否顶上界似乎不太够,因为在转移过程中还需要知道倒过来的数的状态,所以再用个变量(lim2)记录倒过来的数的状况

因为从前向后确定数倒过来看是从后向前确定数,所以(lim2)有三个值:比当前上界小,顶上界,暂时大于上界,用(0,1,2)表示

仅仅这样做并不能很好的处理前缀0的情况(如0001倒过来看会变成1000,但实际上应该为1),所以我还用了一个变量(zer)表示第一个非零数出现的位置

状态即为(f_{i,lim1,lim2,zer}),一遍数位DP即可求出答案,递归边界返回值为1的条件为(lim2 eq 2) 或 倒过来的限制R的位数比L多1

本题毒瘤,(L,R)会炸longlong,需要用unsigned longlong来做,随时都需要注意爆边界的情况qwq

考试的标程都是错的几个意思

#include<bits/stdc++.h>
#define Min(x,y) ((x)<(y)?(x):(y))
#define Max(x,y) ((x)>(y)?(x):(y))
using namespace std;
typedef unsigned long long ll;
int T,t1,t2,d;
ll L,R,wei[25];
ll dp[70][23][2][3];//第一个不是0的位置 
//0:没碰到上界,1:顶上界,2:过上界 

template <class T>
void read(T &x)
{
	char c; int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
}
int gw(ll x,int w)
{
	if(w==20) return 1;
	return (x/wei[w-1])%10;
}
int get_dep(ll x)
{
	int ret=0;
	while(x) {++ret; x/=10;}
	return ret;
}
ll dfs(int stp,int zer,int lim1,int lim2,ll l,ll r)//正数第stp位,第一个非零位置,顶上界,逆向 
{
	if(stp==d+1) return ((lim2!=2 || (stp-zer<=19 && zer && r/wei[stp-zer]) )  &&  zer);
	if(dp[stp][zer][lim1][lim2]!=-1) return dp[stp][zer][lim1][lim2];
	ll ret=0;
	int x=gw(l,d-stp+1),y=gw(r,zer?stp-zer+1:1);
	if(lim1)
	{
		int l2,l3=0;
		for(int i=0;i<=x;++i)
		{
			if(i>y) l2=2;
			else if(i==y) l2=(lim2==2 ? 2 : 1);
			else l2=0;
			if(i) l3=stp;
			ret+=dfs(stp+1,zer?zer:l3,i==x,l2,l,r);
		}
	}
	else
	{
		int l2,l3=0;
		for(int i=0;i<=9;++i)
		{	
			if(i>y) l2=2;
			else if(i==y) l2=(lim2==2 ? 2 : 1);
			else l2=0;
			if(i) l3=stp;
			ret+=dfs(stp+1,zer?zer:l3,0,l2,l,r);
		}
	}
	return dp[stp][zer][lim1][lim2]=ret;
}
ll solve(ll L,ll R)//翻转前<=L,翻转后<=R
{
	if(!L||!R) return 0;
	memset(dp,-1,sizeof(dp));
	d=get_dep(L);
	return dfs(1,0,1,1,L,R);
}
int main()
{
	freopen("reverse.in","r",stdin);
	freopen("reverse.out","w",stdout);
	read(T);read(t1);read(t2);
	wei[0]=1;
	for(int i=1;i<=19;++i) wei[i]=wei[i-1]*10;
	while(T--)
	{
		read(L);read(R);
		cout<<solve(R,R)+solve(L-1,L-1)-solve(R,L-1)-solve(L-1,R)<<endl;
	}
	return 0;
}
原文地址:https://www.cnblogs.com/Chtholly/p/11735111.html