前言
线段树练习赛 T1,最后一分钟调出来了。
所以我T2爆零。
题目
洛谷有翻译。
讲解
首先强制在线已经完全抑制了我们的想象力,而答案太大需要取模,我们只能思考对每个数质因数分解,然后求指数最大值的做法。
对于每个质因数我们都维护一棵线段树显然不现实,因为 (200000) 以内的质数已经多达 (17984) 个。
根据套路,我们将值域分块,(sqrt{200000}approx 447), (447) 以内的质数只有 (86) 个。
对于这 (86) 个数,我们都开一棵线段树(或者一棵线段树维护 (86) 个值),然后询问的时候查询区间最大值即可。
这个部分的时间复杂度为 (O(86nlog_2)),只要写得不算太丑应该没问题。
为了卡常,我还预处理了这 (86) 个数的幂,因为指数显然不会很大,所以这个幂可以预处理。
排除掉 (447) 以内的数之后,每个数只会存在至多一个大于 (447) 的质因数,这里有两个做法。
做法1
这是考试的时候的做法,时间复杂度不优,但是空间更优。
考虑分块。
对整个序列分块,预处理整块之间的最小公倍数,当然这里的序列已经不含 (447) 以内的因数了。
对于散块我们可以使用求区间不同数的经典套路:如果一个数的位置在询问区间中,而且前面与它相同的数的位置在询问区间外,那么才计入贡献。
由于散块在整块的前面和后面都有,所以要预处理前驱和后继,然后暴力判断即可。
时间复杂度为 (O((n+q)sqrt{n})),空间为 (O(n))。
做法2
同样是做法1中的经典套路,对于每个数我们记录 ((pre_i,i)) 这样一组信息,放到线段树的叶子上,然后向上按 (pre_i) 单增进行归并排序。
每次询问时二分区间中最大的一个在询问区间左边的前驱,前缀积即为答案,所以归并的时候还要记录前缀积。
时间复杂度 (O(nlog^2_2n)),空间为 (O(nlog_2n))。
当然用主席树也行。
代码
考试代码
//12252024832524
#include <map>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define TT template<typename T>
using namespace std;
typedef long long LL;
const int MAXN = 200005;
const int MAXB = 450;//其实这里应该是sqrt(n),大概是320
const int MOD = 1000000007;
int n,Q,tp;
int a[MAXN];
LL Read()
{
LL x = 0,f = 1;char c = getchar();
while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
TT void Put1(T x)
{
if(x > 9) Put1(x/10);
putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
if(x < 0) putchar('-'),x = -x;
Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}
bool vis[MAXN];
int prime[MAXN],pn = -1,inv[MAXN];
void sieve(int x)
{
for(int i = 2;i <= x;++ i)
{
if(!vis[i]) prime[++pn] = i;
for(int j = 0;j <= pn && i * prime[j] <= x;++ j)
{
vis[i * prime[j]] = 1;
if(i % prime[j] == 0) break;
}
}
inv[0] = inv[1] = 1;
for(int i = 2;i <= x;++ i) inv[i] = 1ll * (MOD - MOD/i) * inv[MOD%i] % MOD;
}
int MAX[MAXN << 2][86],ri[86];
#define lc (x<<1)
#define rc (x<<1|1)
void Build(int x,int l,int r)
{
if(l == r)
{
a[l] = Read();
for(int i = 0;i < 86;++ i)
while(a[l] % prime[i] == 0) ++MAX[x][i],a[l] /= prime[i];
return;
}
int mid = (l+r) >> 1;
Build(lc,l,mid); Build(rc,mid+1,r);
for(int i = 0;i < 86;++ i)
MAX[x][i] = Max(MAX[lc][i],MAX[rc][i]);
}
void QQ(int x,int l,int r,int ql,int qr)
{
if(ql <= l && r <= qr)
{
for(int i = 0;i < 86;++ i) ri[i] = Max(ri[i],MAX[x][i]);
return;
}
int mid = (l+r) >> 1;
if(ql <= mid) QQ(lc,l,mid,ql,qr);
if(mid+1 <= qr) QQ(rc,mid+1,r,ql,qr);
}
map<int,int> m;
int mi[86][30];
int qpow(int x,int y)
{
int ret = 1;
while(y){if(y & 1) ret = 1ll * ret * x % MOD;x = 1ll * x * x % MOD;y >>= 1;}
return ret;
}
int lst;
int pre[MAXN],suf[MAXN];
int lcm[MAXB][MAXB];
bool ex[MAXN];
void solve2()
{
for(int i = 0;i < 86;++ i)
{
mi[i][0] = 1;
for(int j = 1;j < 30;++ j) mi[i][j] = 1ll * mi[i][j-1] * prime[i] % MOD;
}
Build(1,0,n-1);
m[1] = -1;
for(int i = 0;i <= pn;++ i) m[prime[i]] = -1;
for(int i = 0;i < n;++ i) pre[i] = m[a[i]],m[a[i]] = i;
m.clear();
m[1] = n;
for(int i = 0;i <= pn;++ i) m[prime[i]] = n;
for(int i = n-1;i >= 0;-- i) suf[i] = m[a[i]],m[a[i]] = i;
int B = ceil(sqrt(n));
for(int i = 0;i*B < n;++ i)
{
int l = i*B,cao = 1;
for(int j = i;j*B < n;++ j)
{
int r = Min(n-1,(j+1)*B-1);
for(int k = l;k <= r;++ k)
if(!ex[a[k]]) ex[a[k]] = 1,cao = 1ll * cao * a[k] % MOD;
lcm[i][j] = cao;
l = r+1;
}
for(int j = 0;j <= pn;++ j) ex[prime[j]] = 0;
}
Q = Read();
for(int i = 1;i <= Q;++ i)
{
int l = (lst + Read()) % n,r = (lst + Read()) % n;
if(l > r) swap(l,r);
lst = 1;
for(int j = 0;j < 86;++ j) ri[j] = 0;
QQ(1,0,n-1,l,r);
for(int j = 0;j < 86;++ j) lst = 1ll * lst * mi[j][ri[j]] % MOD;
int LID = l/B,RID = r/B;
if(LID+1 >= RID)
{
for(int j = l;j <= r;++ j)
if(a[j] > 1 && pre[j] < l)
lst = 1ll * lst * a[j] % MOD;
}
else
{
lst = 1ll * lst * lcm[LID+1][RID-1] % MOD;
int ll = l,rr = (LID+1)*B-1,xx = RID*B;
for(int j = ll;j <= rr;++ j)
if(a[j] > 1 && suf[j] >= xx)
lst = 1ll * lst * a[j] % MOD;
ll = RID*B; rr = r;
for(int j = ll;j <= rr;++ j)
if(a[j] > 1 && pre[j] < l)
lst = 1ll * lst * a[j] % MOD;
}
Put(lst,'
');
}
}
int main()
{
// freopen("lcm.in","r",stdin);
// freopen("lcm.out","w",stdout);
sieve(200000);
n = Read();
solve2();
return 0;
}
花絮
代码中的 solve2
函数是在线算法,solve1
是离线算法,只不过被我删掉了(考试的时候离线有60pts)。
也许我不打离线做法就有时间做T2了。