天天看点

bzoj4026 dC Loves Number Theory 主席树DescriptionSolutionCode

Description

dC 在秒了BZOJ 上所有的数论题后,感觉萌萌哒,想出了这么一道水题,来拯救日益枯竭的水题资源。

给定一个长度为 n的正整数序列A,有q次询问,每次询问一段区间内所有元素乘积的φ(φ(n)代表1~n 中与n互质的数的个数) 。由于答案可能很大,所以请对答案 mod 10^6 + 777。

(本题强制在线,所有询问操作的l,r都需要 xor上一次询问的答案 lastans,初始时,lastans = 0)

1 <= N <= 50000

1 <= Q <= 100000

1 <= Ai <= 10^6

Solution

考虑φ的一种求法,注意到一种质因子只会算一次,等价于我们求区间内所有出现至少一次的质因子的贡献

我们开n棵线段树,对于a[i]的一个质因子x,我们在第i棵线段树上删去x在last[x]位置上的贡献,增加x在i上的贡献。这里显然为 x − 1 x \frac{x-1}{x} xx−1​。正确性的话可以yy一下

然后就很简单了,查询l到r就在第r棵线段树上求区间乘积就行了

这道题需要注意下分解质因数的方法。可以记low[i]为i的最小质因子然后每次用low[i]除,可以证明这样是比较优秀的

Code

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

const int MOD=1000777;
const int N=50005;

struct treeNode {
	int l,r,prod;
} t[N*205];

int prime[MOD*2],rec[MOD*2],ny[MOD*2],a[N],p[N];
int last[MOD*2],root[N],low[MOD*2],tot;

bool not_prime[MOD*2];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

int ksm(int x,int dep) {
	int ret=1;
	for (;dep;dep>>=1) {
		(dep&1)?(ret=1LL*ret*x%MOD):0;
		x=1LL*x*x%MOD;
	}
	return ret;
}

void pre_work(int n) {
	ny[1]=1;
	rep(i,2,n) {
		if (!not_prime[i]) {
			prime[++prime[0]]=i;
			ny[i]=ksm(i,MOD-2);
			low[i]=rec[i]=i;
		}
		for (int j=1;1LL*i*prime[j]<=n&&j<=prime[0];++j) {
			not_prime[i*prime[j]]=1;
			ny[i*prime[j]]=1LL*ny[i]*ny[prime[j]]%MOD;
			low[i*prime[j]]=rec[i*prime[j]]=prime[j];
			if (i%prime[j]==0) {
				low[i*prime[j]]=low[i]*prime[j];
				break;
			}
		}
	}
}

void modify(int &now,int pre,int tl,int tr,int x,int v) {
	t[now=++tot]=t[pre]; t[now].prod=1LL*t[now].prod*v%MOD;
	if (tl==tr) return ;
	int mid=(tl+tr)>>1;
	if (x<=mid) modify(t[now].l,t[pre].l,tl,mid,x,v);
	else modify(t[now].r,t[pre].r,mid+1,tr,x,v);
}

int query(int now,int tl,int tr,int l,int r) {
	if (r<l) return 1;
	if (tl==l&&tr==r) return t[now].prod;
	int mid=(tl+tr)>>1;
	if (r<=mid) return query(t[now].l,tl,mid,l,r);
	if (l>mid) return query(t[now].r,mid+1,tr,l,r);
	return 1LL*query(t[now].l,tl,mid,l,mid)*query(t[now].r,mid+1,tr,mid+1,r)%MOD;
}

void build(int &now,int l,int r) {
	t[now=++tot].prod=1;
	if (l==r) return ;
	int mid=(l+r)>>1;
	build(t[now].l,l,mid);
	build(t[now].r,mid+1,r);
}

int main(void) {
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	pre_work(MOD); p[0]=1;
	int n=read(),m=read();
	build(root[0],1,n);
	rep(i,1,n) {
		root[i]=root[i-1];
		a[i]=read(); int tmp=a[i];
		p[i]=1LL*p[i-1]*a[i]%MOD;
		for (;tmp!=1;) {
			int y=low[tmp],x=rec[tmp];
			for (;tmp%y==0;) tmp/=y;
 			if (last[x]) modify(root[i],root[i],1,n,last[x],1LL*ny[x-1]*x%MOD);
			modify(root[i],root[i],1,n,i,1LL*(x-1)*ny[x]%MOD);
			last[x]=i;
		}
	}
	for (int lastans=0;m--;) {
		// int l=read(),r=read();
		int l=read()^lastans,r=read()^lastans;
		lastans=1LL*p[r]*ny[p[l-1]]%MOD;
		lastans=1LL*lastans*query(root[r],1,n,l,r)%MOD;
		printf("%d\n", lastans);
	}
	return 0;
}
                

继续阅读