专栏文章

题解:P13447 [GCJ 2009 #3] Interesting Ranges

P13447题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min4273m
此快照首次捕获于
2025/12/01 20:13
3 个月前
此快照最后确认于
2025/12/01 20:13
3 个月前
查看原文

思路

首先考虑前缀和,设 sis_i 表示 1i1\sim i 中回文数的个数对 22 取模的值,那么区间 [l,r][l,r] 是一个好区间当且仅当 sl1=srs_{l-1}=s_r。现在要求 [L,R][L,R] 有多少个子区间是好区间,若求出 sL1Rs_{L-1\sim R} 中有多少个 0011(记为 cnt0,cnt1cnt_0,cnt_1),那么答案就是 (cnt02)+(cnt12)\binom{cnt_0}2+\binom{cnt_1}2
显然求出 cnt0cnt_0cnt1cnt_1 中任意一个即可知道另一个的值,下面考虑如何求其中的一个。
差分为 1R1\sim R 的个数减去 1L21\sim L-2 的个数,问题转化为求一个前缀的 cnt0cnt_0cnt1cnt_1
若第 ii 个回文数为 aia_i,那么 [ai,ai+1)[a_i,a_{i+1}) 这个区间的 ss 的取值是相等的,对 cnt0/1cnt_{0/1} 的贡献即为 ai+1aia_{i+1}-a_i。考虑从这方面入手。
aia_i 前半段的数为 xx,发现对于 aia_i 位数相同的时候,大部分 ai+1aia_{i+1}-a_{i} 是相等的(例如 1242112321=1141111311=100,123321122221=114411113311=110012421-12321=11411-11311=100,123321-122221=114411-113311=1100),问题出现在 xx 最后一位为 99 时会往前进位(例如 1303112921=11013031-12921=110)。
考虑如何规避最后一位是 99 的情况:
  • xx 位数大于等于 22,设 xx 除去最后一位的数 yy,发现我们可以统计 yy 后跟上一个 090\sim 9 中偶数的答案,这样就不会出现最后一位是 99 的情况。然后发现每个 yy 对应的回文数个数为偶数,所以对于其他的 yy,仍然只需要记录最后一位是偶数的情况;
  • 否则,这个回文数不会超过 100100,直接暴力枚举即可。
发现我们现在记录的个数相当于 cnt1cnt_1,就可以预处理 fif_i 表示 ii回文数的答案。
ii 为奇数,那么两个回文数的间隔即为 10i1210^{\frac{i-1}2},否则为 10i2+10i2110^{\frac i2}+10^{\frac i2-1}。然后考虑 xx 最后一位是偶数的个数,显然为 5×(10i1210i121)5\times(10^{\lfloor\frac {i-1}2\rfloor}-10^{\lfloor\frac {i-1}2\rfloor-1})fif_i 就是这两个数的乘积。特别的,对于 i2i\le 2,需要暴力枚举。
此时考虑计算 1x1\sim x 这个前缀的 cnt1cnt_1。设 xx 的位数为 lenlen,位数小于 lenlen 可以直接使用预处理的 ff 数组。
对于位数等于 lenlen 的情况,经典的,枚举回文数的前一半和 xx 的最长公共前缀长度,钦定下一位必须小于 xx 的那一位,此时分两种情况讨论:
  • 若最长公共前缀长度小于 len12\lfloor\frac{len-1}2\rfloor,最后一定剩下一位可以随便填,那么最后一位就可以取到 090\sim 9 的任意一个偶数。对答案的贡献计算方式和 ff 类似;
  • 否则,最多剩下一位,暴力枚举计算贡献即可。
记得对于 len2len\le 2 的情况特殊处理一下。
时间复杂度 O(len)O(len),其中 lenlen 表示 RR 的位数。

代码

CPP
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+5,mod = 1e9+7,inv2 = (mod+1)>>1;
int pw[N],f[N],sum[N];
inline int get(int x){x = (x+mod)%mod;return x*(x-1)%mod*inv2%mod;}
inline int get(string &s)
{
	int res = 0;
	for(int i = 0;i<s.size();i++) (res+=pw[s.size()-i-1]*(s[i]-'0'))%=mod;
	return res;
}
string s,t;
inline int work()
{
	if(s.size()&&s[0]<'0') return 0;
	if(s.size()==0) return 0;
	if(s.size()==1) return sum[s[0]-'0'];
	if(s.size()==2)
	{
		int now = (s[0]-'0')*10+s[1]-'0';
		return sum[now];
	}
	int n = s.size();
	int res = 0;
	for(int i = 0;i<n;i++) (res+=f[i])%=mod;
	bool fl = 1;
	int w = pw[n/2];
	if(n%2==0) (w+=pw[n/2-1])%=mod;
	for(int i = 0;i<(n-1)/2;i++)
	{
		int x = pw[(n-2*(i+1)-1)/2];
		(res+=x*w%mod*5*(s[i]-'0'-(i==0)))%=mod;
	}
	t.resize(n);
	for(int i = 0;i<10;i+=2)
	{
		for(int j = 0;j<(n-1)/2;j++)
			t[j] = t[n-j-1] = s[j];
		t[(n-1)/2] = i+'0';
		if(n%2==0) t[n/2] = i+'0';
		bool fl1 = (t<=s);
		t[(n-1)/2]++;
		if(n%2==0) t[n/2]++;
		bool fl2 = (t<=s);
		if(fl2)
		{
			(res+=w)%=mod;
		}
		else if(fl1)
		{
			t[(n-1)/2]--;
			if(n%2==0) t[n/2]--;
			(res+=get(s)-get(t)+1+mod)%=mod;
		}
		else break;
	}
	return res;
}
inline void solve()
{
	cin>>s;
	s[s.size()-1]-=2;
	int p = s.size()-1;
	while(p>0&&s[p]<'0')
	{
		s[p]+=10;
		s[p-1]--;
	}
	if(s[0]=='0') s.erase(s.begin());
	int ans = mod-work(),len = mod-get(s);
	cin>>s;
	(ans+=work())%=mod;
	(len+=get(s))%=mod;
	cout<<(get(ans)+get(len-ans))%mod<<'\n';
}
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	pw[0] = 1;
	for(int i = 1;i<N;i++) pw[i] = pw[i-1]*10%mod;
	int now = 0;
	for(int i = 1;i<100;i++)
	{
		if(i<10||i%11==0) now^=1;
		sum[i] = sum[i-1]+now;
	}
	f[1] = sum[9];
	f[2] = sum[99]-sum[9];
	for(int i = 3;i<N;i++)
	{
		int cnt = (pw[(i-1)/2]-pw[(i-1)/2-1]+mod)%mod,w = pw[i/2];
		if(i%2==0) (w+=pw[i/2-1])%=mod;
		f[i] = cnt*w%mod*5%mod;
	}
	int T;cin>>T;
	for(int _ = 1;_<=T;_++)
	{
		cout<<"Case #"<<_<<": ";
		solve();
	}
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...