专栏文章
题解:P13447 [GCJ 2009 #3] Interesting Ranges
P13447题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @min4273m
- 此快照首次捕获于
- 2025/12/01 20:13 3 个月前
- 此快照最后确认于
- 2025/12/01 20:13 3 个月前
思路
首先考虑前缀和,设 表示 中回文数的个数对 取模的值,那么区间 是一个好区间当且仅当 。现在要求 有多少个子区间是好区间,若求出 中有多少个 和 (记为 ),那么答案就是 。
显然求出 和 中任意一个即可知道另一个的值,下面考虑如何求其中的一个。
差分为 的个数减去 的个数,问题转化为求一个前缀的 或 。
若第 个回文数为 ,那么 这个区间的 的取值是相等的,对 的贡献即为 。考虑从这方面入手。
设 前半段的数为 ,发现对于 位数相同的时候,大部分 是相等的(例如 ),问题出现在 最后一位为 时会往前进位(例如 )。
考虑如何规避最后一位是 的情况:
- 若 位数大于等于 ,设 除去最后一位的数 ,发现我们可以统计 后跟上一个 中偶数的答案,这样就不会出现最后一位是 的情况。然后发现每个 对应的回文数个数为偶数,所以对于其他的 ,仍然只需要记录最后一位是偶数的情况;
- 否则,这个回文数不会超过 ,直接暴力枚举即可。
发现我们现在记录的个数相当于 ,就可以预处理 表示 位回文数的答案。
若 为奇数,那么两个回文数的间隔即为 ,否则为 。然后考虑 最后一位是偶数的个数,显然为 。 就是这两个数的乘积。特别的,对于 ,需要暴力枚举。
此时考虑计算 这个前缀的 。设 的位数为 ,位数小于 可以直接使用预处理的 数组。
对于位数等于 的情况,经典的,枚举回文数的前一半和 的最长公共前缀长度,钦定下一位必须小于 的那一位,此时分两种情况讨论:
- 若最长公共前缀长度小于 ,最后一定剩下一位可以随便填,那么最后一位就可以取到 的任意一个偶数。对答案的贡献计算方式和 类似;
- 否则,最多剩下一位,暴力枚举计算贡献即可。
记得对于 的情况特殊处理一下。
时间复杂度 ,其中 表示 的位数。
代码
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 条评论,欢迎与作者交流。
正在加载评论...