【CF594D】REQ

题目

题目链接:https://codeforces.com/problemset/problem/594/D
给出一个长度为 \(n\) 的数列,\(Q\) 次询问,每次询问给出 \(l,r\),需要求出 \(\varphi(\prod ^{r}_{i-l}a[i])\bmod p\)。
\(n,Q\leq 200000,a[i]\leq 10^6\)。

思路

考虑到一个数 \(x\) 的质因子不会超过 \(\log x\) 个,所以可以离线按照询问的右端点排序,记录每一个质因子最后出现的位置。
按照右端点排序后,我们对于一个询问 \([l_i,r_i]\),将 \((r_{i-1},r_i]\) 的数分别求质因子,然后找到上一个拥有这个质因子的数,用一个数据结构维护一下,保证对于每一个质因子 \(p\),它的贡献都算在现在询问到的区间的最后一个含有 \(p\) 质因子的数字上。而一个数 \(p\) 的贡献是 \(\frac{p-1}{p}\),所以我们需要一个支持单调修改、区间查询乘积的数据结构,线段树和树状数组都可以的。
如何快速处理出一个数的质因子?在线性筛的时候,我们求出了每个数的最小质因子 \(v\),那么就不断让 \(a[i]\) 除以 \(v[a[i]]\) 即可。
讲的似乎很模糊,看一下这部分代码

for (;j<=ask[i].r;j++)
	for (int k=a[j];k>1;)
	{
		int d=v[k];  //求这一位的质因子
		if (last[d]) seg.update(1,last[d],d);
		if (last[d]) seg.update(1,last[d],inv[d-1]);  //在线段树中将上一个含有这个质因子的位置的贡献消除
		last[d]=j;
		seg.update(1,j,inv[d]);
		seg.update(1,j,d-1);  //维护该质因子的贡献
		while (k%d==0) k/=d;
	}

那么整个问题就迎刃而解了。需要预处理逆元,否则时间复杂度多一个 \(\log n\)。
时间复杂度 \(O(n\log n\log a[i])\)。

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N=200010,M=1e6+10,MOD=1e9+7;
int n,Q,m,a[N],last[M],v[M],prm[M],inv[M],ans[N];

struct Query
{
	int l,r,id;
}ask[N];

bool cmp(Query x,Query y)
{
	return x.r<y.r;
}

void findprm(int k)
{
	for (int i=2;i<=k;i++)
	{
		if (!v[i]){v[i]=i; prm[++m]=i;}
		for (int j=1;j<=m;j++)
		{
			if (1LL*prm[j]*i>k || v[i]<prm[j]) break;
			v[prm[j]*i]=prm[j];
		}
	}
}

ll fpow(ll x,ll k,ll p)
{
	ll ans=1;
	for (;k;k>>=1,x=x*x%p)
		if (k&1) ans=ans*x%p;
	return ans;
}

struct Seg
{
	int l[N*4],r[N*4];
	ll mul[N*4];
	
	void pushup(int x)
	{
		mul[x]=mul[x*2]*mul[x*2+1]%MOD;
	}
	
	void build(int x,int ql,int qr)
	{
		l[x]=ql; r[x]=qr;
		if (ql==qr)
		{
			mul[x]=a[ql];
			return;
		}
		int mid=(ql+qr)>>1;
		build(x*2,ql,mid); build(x*2+1,mid+1,qr);
		pushup(x);
	}
	
	void update(int x,int k,int val)
	{
		if (l[x]==k && r[x]==k)
		{
			mul[x]=mul[x]*val%MOD;
			return;
		}
		int mid=(l[x]+r[x])>>1;
		if (k<=mid) update(x*2,k,val);
			else update(x*2+1,k,val);
		pushup(x);
	}
	
	ll query(int x,int ql,int qr)
	{
		if (l[x]==ql && r[x]==qr)
			return mul[x];
		int mid=(l[x]+r[x])>>1;
		if (qr<=mid) return query(x*2,ql,qr);
		else if (ql>mid) return query(x*2+1,ql,qr);
		else return query(x*2,ql,mid)*query(x*2+1,mid+1,qr)%MOD;
	}
}seg;

int main()
{
	findprm(M-10);
	inv[0]=1;
	for (int i=1;i<=M-10;i++)
		inv[i]=fpow(i,MOD-2,MOD);
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	seg.build(1,1,n);
	scanf("%d",&Q);
	for (int i=1;i<=Q;i++)
	{
		scanf("%d%d",&ask[i].l,&ask[i].r);
		ask[i].id=i;
	}
	sort(ask+1,ask+1+Q,cmp);
	for (int i=1,j=1;i<=Q;i++)
	{
		for (;j<=ask[i].r;j++)
			for (int k=a[j];k>1;)
			{
				int d=v[k];
				if (last[d]) seg.update(1,last[d],d);
				if (last[d]) seg.update(1,last[d],inv[d-1]);
				last[d]=j;
				seg.update(1,j,inv[d]);
				seg.update(1,j,d-1);
				while (k%d==0) k/=d;
			}
		ans[ask[i].id]=seg.query(1,ask[i].l,ask[i].r);
	}
	for (int i=1;i<=Q;i++)
		printf("%d\n",ans[i]);
	return 0;
}
上一篇:Codeforces Round #587 (Div. 3) F. Wi-Fi(dp+线段树)


下一篇:「CF1023F」Mobile Phone Network