[FJWC2018]欧拉函数

欧拉函数

题目描述

在这里插入图片描述
在这里插入图片描述

题解

首先,我们关注到这一题最重要的一点是保证所有的 a , i , x , l , r a,i,x,l,r a,i,x,l,r均随机生成。
对于操作 1 1 1,每次询问的区间和大小期望大概是 n a 4 ≈ 5 × 1 0 8 \frac{na}{4}\approx 5\times 10^8 4na5×108,区间的和。
如果我们暴力算欧拉函数大概是 O ( 5 × 1 0 8 log ⁡ 5 × 1 0 8 ≈ 4152.3 ) O(\sqrt{\frac{5\times 10^8}{\log 5\times 10^8}}\approx 4152.3) O(log5×1085×108 4152.3)的,对于 q = 1 0 5 q=10^5 q=105,应该也勉强能过。
由于总和是随机的,所以应该也不需要所有质数都跑完,会更快一点。
关键是操作 2 2 2,对于乘法我们又该怎样统计。
由于随机生成,所以我们可以猜测每个数所拥有的大于 30 30 30的质因子个数应该比较少,每个数大概期望有 1.05 1.05 1.05个。
故我们很快可以想到将小于 30 30 30的质数贡献与大于 30 30 30的质数贡献分别进行处理。

对于小于 30 30 30的质数,由于个数比较小,我们可以考虑用线段树单独维护每个质因子的出现次数。
由于欧拉函数是积性函数,所以我们显然可以对于单个质因子分别统计欧拉函数值,最后乘起来。
询问 2 2 2查询时就直接查询区间内这部分质数出现了多少个,计算该质数贡献的欧拉函数就行了,大概是 O ( 10 log ⁡   n ) O\left(10\log\,n\right) O(10logn)的。

对于大于 30 30 30的质数,由于本身就比较多,相同质因子的应该也比较分散。
对于一个数的质因子 p p p,如果它前面有数也存在 p p p的质因子,那么它的贡献时 p p p,否则是 p − 1 p-1 p1
对于前面是否存在数有质因子 p p p,显然我们只需要看离它最近的一个 p p p是不是在我们查询的区间范围内。
我们在查询到它的时候可以在前面的 p p p的位置放上一个 p p − 1 \frac{p}{p-1} p1p,在它原来的位置就放 p − 1 p-1 p1,查询左边界的后缀积,显然只会在包含前面的哪个位置时它的贡献才会是 p p p,否则其贡献为 p − 1 p-1 p1
这种技巧我们应该是在前面的题中见到过得,但我懒得找了。
但如果我们真的一个一个去查询的话显然太慢了,每一个都得拿一个数据结构维护,我们不妨考虑分块。
对于块内的点,如果它们不存在大于 30 30 30的公共质因子,显然它们之间是独立的,我们完全可以将它们维护的这个贡献给放在一起。
如果同一个块内两个数有着相同的质因子 p − 1 p-1 p1,显然后面那一个是不可能产生 p − 1 p-1 p1的贡献的,我们就将这个整块的贡献乘上 p ( p − 1 ) p(p-1) p(p1),再在前一个数的前一个相同质因子位置上放上 p p − 1 \frac{p}{p-1} p1p就行了。
这样的话,我们对于一个整块就只需要维护一个数据结构了,这个数据结构采用树状数组就行了。
对于散块的点,我们需要知道的是它们前面是否有与它相同的质因子的数,这个数既可能来自整块的数,也有了能来自散块的数。
散块的数就直接将它们大于 30 30 30的质因子记录下来就行了,对于整块,我们可以去记录一下整块的数关于大于 30 30 30的每个质数的前缀和,通过差分找到我们现在的这个质数是否出现。
由于我们的修改次数是比较少的,我们可以在每次修改后暴力修正修改的这个质数的前缀和。
同时修改时对于整块我们可能会影响它自身与它相同质因子的后面最近的点关于整块数据结构内的贡献。
可以用 s e t set set维护它后面的点是哪一个,然后暴力在数据结构上更改。
这样就很容易计算出整块与散块的贡献了,与小于 30 30 30质数的贡献乘在一起就行了。

记我们进行了 m m m 0 0 0类操作,有 P P P个质数, S S S为块长,时间复杂度 O ( n S P + m ( n S + 10 log ⁡   n ) + q ( 10 log ⁡   n + S + n S log ⁡   n ) ⩾ O ( 10 ( m + q ) log ⁡   n + q n log ⁡   n ) ) O\left(\frac{n}{S}P+m(\frac{n}{S}+10\log\,n)+q(10\log\,n+S+\frac{n}{S}\log\,n)\geqslant O\left(10(m+q)\log\,n+q\sqrt{n\log\,n}\right)\right) O(SnP+m(Sn+10logn)+q(10logn+S+Snlogn)O(10(m+q)logn+qnlogn )),显然可以过。

源码

有点卡常。

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define MAXN 50005
#define MAXM 500005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL; 
typedef long double ld;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int mo=1e9+7;
const int mod=1e5+3;
const int inv2=5e8+4;
const int jzm=2333;
const int zero=20000;
const int n1=400;
const int orG=3,ivG=332748118;
const long double Pi=acos(-1.0);
const double eps=1e-12;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
char gc(){static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
#define getchar gc
char obuf[1<<22],*opt=obuf+(1<<22);
void pc(const int&ch){*--opt=ch;}
#define putchar pc
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){putchar('\n');while(x>9){putchar((x%10)|'0');x/=10;}putchar(x|'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;}return t;}
const int M=255;
int n,q,a[MAXN],prime[MAXN],cntp,ff[MAXN],sta[MAXN],stak,tur[MAXN];
int block[MAXN],L[MAXN],R[MAXN],summ[MAXN],cnt[M][5005],ans[MAXN*2],tott;
int head[MAXN],nxt[MAXN*10],val[MAXN*10],tot;
set<int>s[MAXN];
set<int>::iterator it;
bool oula[MAXN],vis[MAXN];
void init(){
	for(int i=2;i<=4e4;i++){
		if(!oula[i]){prime[++cntp]=i,tur[i]=cntp;}
		for(int j=1;j<=cntp&&1ll*prime[j]*i<=4e4;j++){
			oula[i*prime[j]]=1;
			if(i%prime[j]==0)break;
		}
	}
	ff[1]=1;for(int i=2;i<=4e4;i++)ff[i]=1ll*(mo-mo/i)*ff[mo%i]%mo;
}
void work(int id){
	head[id]=0;int now=a[id];
	for(int i=1;i<=cntp&&1ll*prime[i]*prime[i]<=now;i++){
		if(now%prime[i])continue;
		if(i>10){val[++tot]=prime[i];nxt[tot]=head[id];head[id]=tot;}
		while(now%prime[i]==0)now/=prime[i];
	}
	if(now>prime[10]){val[++tot]=now;nxt[tot]=head[id];head[id]=tot;}
}
class BitTree{
	private:
		int tr[MAXN];
	public:
		void insert(int pos,int aw){while(pos<=n)tr[pos]+=aw,pos+=lowbit(pos);}
		int query(int pos){int res=0;while(pos)res+=tr[pos],pos-=lowbit(pos);return res;}
}pT[26],T;
class MulBitTree{
	private:
		int tr[MAXN],ip;
	public:
		void build(int id){ip=id;for(int i=1;i<=n;i++)tr[i]=1;}
		void insert(int pos,int aw){while(pos)tr[pos]=1ll*aw*tr[pos]%mo,pos-=lowbit(pos);}
		int query(int pos){int res=1;while(pos<=n)res=1ll*res*tr[pos]%mo,pos+=lowbit(pos);return res;}
}Bp[M];
void misakaAdd(int x,const int y,const int z){
	if(y==block[x])
		summ[y]=1ll*z*summ[y]%mo;
	else summ[y]=1ll*(z-1)*summ[y]%mo,
		Bp[y].insert(x,1ll*ff[z-1]*z%mo);
}
void misakaDel(int x,const int y,const int z){
	if(y==block[x])
		summ[y]=1ll*ff[z]*summ[y]%mo;
	else summ[y]=1ll*ff[z-1]*summ[y]%mo,
		Bp[y].insert(x,1ll*ff[z]*(z-1)%mo);
}
int sakura(int l,int r){
	int res=1;
	for(int j=l;j<=r;j++){
		int now=a[j];
		for(int k=head[j];k;k=nxt[k]){
			const int z=val[k],ip=tur[z];int num=0;
			while(now%z==0){
				now/=z;num++;
				if(vis[ip])res=1ll*z*res%mo;
				else res=1ll*(z-1)*res%mo,vis[ip]=1,sta[++stak]=ip;
			}
		}
	}
	return res;
}
int sakura2(int l,int r,const int al,const int ar){
	int res=1;
	for(int j=l;j<=r;j++){
		int now=a[j];
		for(int k=head[j];k;k=nxt[k]){
			const int z=val[k],ip=tur[z];int num=0;
			while(now%z==0){
				now/=z;num++;
				if(vis[ip]||cnt[ar][ip]>cnt[al][ip])res=1ll*z*res%mo;
				else res=1ll*(z-1)*res%mo,vis[ip]=1,sta[++stak]=ip;
			}
		}
	}
	return res;
}
signed main(){
	freopen("phi.in","r",stdin);
	freopen("phi.out","w",stdout);
	read(n);read(q);init();
	for(int i=1;i<=n;i++)read(a[i]);
	for(int i=1;i<=n;i++)block[i]=(i+n1-1)/n1;
	for(int i=1;i<=n;i++){if(!L[block[i]])L[block[i]]=i;R[block[i]]=i;}
	for(int i=1;i<=block[n];i++)Bp[i].build(i),summ[i]=1;
	for(int i=1;i<=n;i++){
		T.insert(i,a[i]);int now=a[i];work(i);
		for(int j=1;j<=10;j++){
			int tmp=0;while(now%prime[j]==0)now/=prime[j],tmp++;
			if(tmp)pT[j].insert(i,tmp);
		}
		const int ip=block[i];
		for(int j=head[i];j;j=nxt[j]){
			int num=0;const int x=val[j];
			while(now%x==0){
				s[x].insert(3*i+num);now/=x;
				it=s[x].lower_bound(3*i+num);num++;
				if(it!=s[x].begin()){it--;int vp=(*it)/3;misakaAdd(vp,ip,x);}
				else summ[ip]=1ll*(x-1)*summ[ip]%mo;
			}
			cnt[ip][tur[x]]+=num;
		}
	}
	for(int i=2;i<=block[n];i++)
		for(int j=11;j<=cntp;j++)cnt[i][j]+=cnt[i-1][j];
	for(int i=1;i<=q;i++){
		int opt,x,y;read(opt);read(x);read(y);
		if(opt==0){
			for(int j=1;j<=10;j++){
				int nowx=a[x],nowy=y,numx=0,numy=0; 
				while(nowx%prime[j]==0)nowx/=prime[j],numx++;
				while(nowy%prime[j]==0)nowy/=prime[j],numy++;
				pT[j].insert(x,numy-numx);
			}
			int now=a[x];const int ip=block[x];
			for(int j=head[x];j;j=nxt[j]){
				int num=0;const int z=val[j];
				while(now%z==0){
					it=s[z].lower_bound(3*x+num);now/=z;int pp=0;
					if(it!=s[z].begin()){it--;int vp=(*it)/3;pp=vp;misakaDel(vp,ip,z);it++;}
					else summ[ip]=1ll*ff[z-1]*summ[ip]%mo;
					it++;
					if(it!=s[z].end()){
						int vp=(*it)/3;misakaDel(x,block[vp],z);
						if(pp)misakaAdd(pp,block[vp],z);
						else summ[block[vp]]=1ll*(z-1)*summ[block[vp]]%mo;
					}
					s[z].erase(3*x+num);num++;
				}
				for(int k=ip;k<=block[n];k++)cnt[k][tur[z]]-=num;
			}
			T.insert(x,y-a[x]);a[x]=y;work(x);now=y;
			for(int j=head[x];j;j=nxt[j]){
				int num=0;const int z=val[j];
				while(now%z==0){
					s[z].insert(3*x+num);
					it=s[z].lower_bound(3*x+num);num++;now/=z;int pp=0;
					if(it!=s[z].begin()){it--;int vp=(*it)/3;pp=vp;misakaAdd(vp,ip,z);it++;}
					else summ[ip]=1ll*(z-1)*summ[ip]%mo;
					it++;
					if(it!=s[z].end()){
						int vp=(*it)/3;
						if(pp)misakaDel(pp,block[vp],z);
						else summ[block[vp]]=1ll*ff[z-1]*summ[block[vp]]%mo;
						misakaAdd(x,block[vp],z);
					}
				}
				for(int k=ip;k<=block[n];k++)cnt[k][tur[z]]+=num;
			}
		}
		if(opt==1){
			int tmp=T.query(y)-T.query(x-1),res=1;
			for(int j=1;j<=cntp&&1ll*prime[j]*prime[j]<=tmp;j++){
				if(tmp%prime[j])continue;int num=0;
				while(tmp%prime[j]==0)tmp/=prime[j],num++;
				res=1ll*(prime[j]-1)*qkpow(prime[j],num-1,mo)%mo*res%mo;
			}
			if(tmp>1)res=1ll*(tmp-1)*res%mo;ans[++tott]=res;
		}
		if(opt==2){
			int res=1;
			for(int j=1;j<=10;j++){
				int tmp=pT[j].query(y)-pT[j].query(x-1);
				if(!tmp)continue;res=1ll*(prime[j]-1)*res%mo;
				res=1ll*res*qkpow(prime[j],tmp-1,mo)%mo;
			}
			if(block[x]==block[y]){
				res=1ll*res*sakura(x,y)%mo;
				ans[++tott]=res;while(stak)vis[sta[stak--]]=0;
				continue;
			}
			res=1ll*res*sakura(x,R[block[x]])%mo;
			for(int j=block[x]+1;j<block[y];j++)
				res=1ll*res*summ[j]%mo,res=1ll*res*Bp[j].query(x)%mo;
			res=1ll*res*sakura2(L[block[y]],y,block[x],block[y]-1)%mo;
			ans[++tott]=res;while(stak)vis[sta[stak--]]=0;
		}
	}
	while(tott)print(ans[tott--]);
	fwrite(opt,1,obuf+(1<<22)-opt,stdout);
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值