题解:P8249 模法问题
Augustus_Deception · · 题解
P8249 模法问题 题解
前言
ST表不错的练习题,感觉有点考验注意力。
题目大意
给定两个正整数
解题思路
关键观察
- 函数
f(i) = (i \bmod a) + (i \bmod b) 具有周期性。 - 周期为
L = \operatorname{lcm}(a,b) 。 - 在
[0, L-1] 范围内,f(i) 的取值具有代表性。
算法设计
- 预处理
L = \operatorname{lcm}(a,b) 。 - 计算
f(i) 在i=0,1,\dots,L-1 的值。 - 构建:
- 前缀最大值数组
pre。 - 后缀最大值数组
suf。 - ST表用于区间最值查询。
- 前缀最大值数组
- 对于每个询问
[l,r] :- 若区间长度
\ge L ,直接返回全局最大值。 - 否则,将区间映射到
[0, L-1] 内:- 若
l \bmod L \le r \bmod L ,使用ST表查询区间最大值。 - 否则,
ans = \max( 后缀[l \bmod L] , 前缀[r \bmod L]) 。
- 若
- 若区间长度
时间复杂度
- 预处理:
O(L \log L) ,其中L \le 10^6 。 - 每次查询:
O(1) 。 - 总复杂度:
O(L \log L + q) ,显然是可以通过的。
代码实现
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
int a,b,L,q,mx;
int v[N],pre[N],suf[N],st[20][N],lg[N];
inline int lcm(int x,int y){return x/__gcd(x,y)*y;}
void build()
{
lg[1]=0;
for(int i=2;i<=L;i++) lg[i]=lg[i>>1]+1;
for(int i=0;i<L;i++) st[0][i]=v[i];
for(int j=1;(1<<j)<=L;j++)
for(int i=0;i+(1<<j)-1<L;i++) st[j][i]=max(st[j-1][i],st[j-1][i+(1<<(j-1))]);
}
int query(int l,int r)
{
int k=lg[r-l+1];
return max(st[k][l],st[k][r-(1<<k)+1]);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>a>>b;
L=lcm(a,b);
for(int i=0;i<L;i++)
{
v[i]=i%a+i%b;
mx=max(mx,v[i]);
pre[i]=mx;
}
suf[L-1]=v[L-1];
for(int i=L-2;i>=0;i--) suf[i]=max(suf[i+1],v[i]);
build();
cin>>q;
while(q--)
{
ll l,r;
cin>>l>>r;
if(r-l+1>=L)
{
cout<<pre[L-1]<<'\n';
continue;
}
int lmod=l%L,rmod=r%L;
if(lmod<=rmod)
{
cout<<query(lmod,rmod)<<'\n';
}
else
{
int ans=max(suf[lmod],pre[rmod]);
cout<<ans<<'\n';
}
}
return 0;
}