51nod 1989 竞赛表格(信仰题)

105 篇文章 0 订阅
92 篇文章 0 订阅

定义 r e v ( i ) rev(i) rev(i)为i十进制下各位翻转所得的数,例如 r e v ( 2345 ) = 5432 , r e v ( 3210 ) = 123 rev(2345)=5432,rev(3210)=123 rev(2345)=5432rev(3210)=123
l0nl1f3比较无聊,他打算用 r e v rev rev函数进行一个游戏。
他找了一个无穷大的表格,设第i行第j列的格子上的数为f(i,j),那么f满足
f ( x , y ) = x , y = 1 f(x,y)=x , y=1 f(x,y)=x,y=1
f ( x , y ) = f ( x , y − 1 ) + r e v ( f ( x , y − 1 ) ) , y > 1 ​ f(x,y) = f(x,y−1)+rev(f(x,y−1)),y>1​ f(x,y)=f(x,y1)+rev(f(x,y1)),y>1
l0nl1f3想要知道这个表格中 [ L , R ] [L,R] [L,R]之间的数出现了多少次, m o d   P mod\ P mod P输出。
由于他有时候会改变主意,所以他可能会询问q次,每次的L、R、P可能不同。
1 ≤ q ≤ 1 0 5 , 1 ≤ L ≤ R ≤ 1 0 10 , 1 ≤ P ≤ 1 0 9 1≤q≤10^5,1≤L≤R≤10^{10},1≤P≤10^9 1q1051LR10101P109

这题很有NOIP模拟赛的意味啊(我说的是那种队爷都做不起的NOIPlus模拟赛)
首先小学奥数一波:
a b c d e ‾ + e d c b a ‾ = 10001 ( a + e ) + 1010 ( b + d ) + 200 c \overline {abcde} + \overline {edcba} = 10001(a+e)+1010(b+d)+200c abcde+edcba=10001(a+e)+1010(b+d)+200c
从这个式子我们可以发现,可以被表示为 x + r e v ( x ) x+rev(x) x+rev(x)的数应该是比较少的吧。
事实证明在 1 0 10 10^{10} 1010内有 3 ∗ 1 0 6 3*10^6 3106个,先用上面的式子搜出来(枚举 a + e a+e a+e, b + d b+d b+d…)
然后我们再考虑每个数出现了多少次。
x + r e v ( x ) x+rev(x) x+rev(x)会出现在 f ( x , j ) f(x,j) f(x,j)中,同时 ( x + r e v ( x ) ) + r e v ( x + r e v ( x ) ) (x+rev(x))+rev(x+rev(x)) (x+rev(x))+rev(x+rev(x))也会,以此类推。
发现这 x + r e v ( x ) x+rev(x) x+rev(x)递增,也就是这种关系有着及其强的拓扑关系。
d p [ x ] dp[x] dp[x]为x在 f ( y , j ) , y &lt; x f(y,j),y&lt;x f(y,j),y<x中出现的次数。
那么 d p [ x + r e v ( x ) ] + = d p [ x ] + 1 dp[x+rev(x)] += dp[x]+1 dp[x+rev(x)]+=dp[x]+1
这里的 x x x是任意 1 0 10 10^{10} 1010内的数,显然会炸。
只需要在前面搜索的时候算一下 g ( x ) = ∑ y + r e v ( y ) = x 1 g(x) = \sum_{y+rev(y)=x} 1 g(x)=y+rev(y)=x1
然后 d p [ x + r e v ( x ) ] + = d p [ x ] dp[x+rev(x)] += dp[x] dp[x+rev(x)]+=dp[x]这时候的 x x x范围就是可以被表示为 y + r e v ( y ) y+rev(y) y+rev(y)的数,一共 3 ∗ 1 0 6 3*10^6 3106个。
然后前缀和一下回答询问就行了。
时间复杂度: O ( 3 ∗ 1 0 6 lg ⁡ ( 3 ∗ 1 0 6 ) ) O(3*10^6\lg{(3*10^6)}) O(3106lg(3106))我也不知道sort为啥这么快
AC Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;

char ch,B[1<<20],*S=B,*T=B;
#define getc() (S==T&&(T=(S=B)+fread(B,1,1<<20,stdin),S==T)?0:*S++)
#define isd(c) (c>='0'&&c<='9')
ll aa;ll F(){ //positive only.
    while(ch=getc(),!isd(ch));aa=ch-'0';
    while(ch=getc(),isd(ch))aa=aa*10+ch-'0';
	return aa;
}

ll psb[3200000],sb[3200000],pw[20],psum[3200000],sum[3200000];
int c[3200000];
void dfs(int now,int lim,ll had,ll ways){
	if(had > pw[10]) return;
	if(now == (lim+1)/2){
		psb[++psb[0]] = had , c[psb[0]] = psb[0] , psum[psb[0]] = ways;
		//if(had == 110)
		//	printf("%d %d %lld %lld\n", now,lim,had,ways);
		return;
	}
	if(!now){
		if(pw[now] == pw[lim-now-1]){
			for(int i=1;i<=9;i++)
				dfs(now+1,lim,had+2*i*pw[now],ways);
		}
		else{
			for(int i=1;i<=18;i++)
				dfs(now+1,lim,had+i*(pw[now]+pw[lim-1-now]),ways*(i<=9?i:9-(i-10)));
		}
	}
	else{
		if(pw[now] == pw[lim-now-1]){
			for(int i=0;i<=9;i++)
				dfs(now+1,lim,had+2*i*pw[now],ways);
		}
		else{
			for(int i=0;i<=18;i++)
				dfs(now+1,lim,had+i*(pw[now]+pw[lim-1-now]),ways*(i<=9?i+1:9-(i-10)));
		}
	}
}

ll rev(ll x){
	ll ret=0;
	for(;x;) ret=ret*10+x%10,x/=10;
	return ret;
}

bool cmp(const int &u,const int &v){ return psb[u] < psb[v]; }

int main(){
	//freopen("1.in","r",stdin);
	//freopen("1.out","w",stdout);
	pw[0] = 1;
	for(int i=1;i<=10;i++) pw[i] = pw[i-1] * 10;
	for(int i=1;i<=10;i++) dfs(0,i,0,1);
	sort(c+1,c+1+psb[0],cmp);
	for(int i=1;i<=psb[0]+1;i++)
		if(psb[c[i]] == psb[c[i-1]])
			psum[c[i]] += psum[c[i-1]];
		else if(i>1)
			sb[++sb[0]] = psb[c[i-1]] , sum[sb[0]] = psum[c[i-1]];
	
	//for(int i=1;i<=200;i++)
		//printf("%d %lld %lld\n",i,sb[i],sum[i]);
	for(int i=1;i<=sb[0];i++){
		//if(sb[i]+rev(sb[i]) == 11) 
		//	printf("%d\n",sb[i]);
		sum[lower_bound(sb+1,sb+1+sb[0],sb[i]+rev(sb[i]))-sb] += sum[i];
		sum[i] += sum[i-1];
		//if(i<=200)printf("%d %lld\n",i,sum[i]);
	}
	
	int q=F(),ans=0;
	ll l,r,p;
	for(;q--;){
		l=F(),r=F(),p=F();
		int ret = (sum[upper_bound(sb+1,sb+1+sb[0],r)-sb-1] - sum[upper_bound(sb+1,sb+1+sb[0],l-1)-sb-1] + r - l + 1) % p;
		//printf("%d\n",ret);
		ans ^= ret;
	}
	printf("%d\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值