• [CF1422F] Boring Queries


    前言

    线段树练习赛 T1,最后一分钟调出来了。

    所以我T2爆零。

    题目

    洛谷

    CF

    洛谷有翻译。

    讲解

    首先强制在线已经完全抑制了我们的想象力,而答案太大需要取模,我们只能思考对每个数质因数分解,然后求指数最大值的做法。

    对于每个质因数我们都维护一棵线段树显然不现实,因为 (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了。

  • 相关阅读:
    git(1)-git关联GitHub-windows-转载
    jenkins(4)-jenkins配置邮件通知
    jenkins(3)-linux下安装jenkins(yum install方式)
    【PAT甲级】1090 Highest Price in Supply Chain (25 分)(DFS)
    【PAT甲级】1087 All Roads Lead to Rome (30 分)(MAP【int,string】,邻接表,DFS,模拟,SPFA)
    【PAT甲级】1018 Public Bike Management (30 分)(DFS,SPFA)
    Educational Codeforces Round 61 (Rated for Div. 2) G(线段树,单调栈)
    Atcoder Grand Contest 032C(欧拉回路,DFS判环)
    Educational Codeforces Round 62 (Rated for Div. 2)E(染色DP,构造,思维,组合数学)
    Atcoder Grand Contest 031C(构造,思维,异或,DFS)
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15209054.html
Copyright © 2020-2023  润新知