[CSP校内集训]reverse(数位DP)
题意
给一个范围[L,R],求满足\(L\leq n \leq R\) 且 \(L\leq rev(n) \leq R\)的\(n\)的个数,其中\(rev(n)\)表示将\(n\)翻转\((123->321)\),多组询问\((L,R\leq 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 \neq 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;
}