题解 欧拉函数

传送门

把智商下限秀完了……

考场上试图打暴力,然后发现空间开不下挂成 \(35pts\)
于是……

  • 线性筛预处理分解质因数的时候,即使只记录 \(low\) 数组分解的过程也是log的,\(lowp\) 只是用来卡常或保证线性筛严格 \(O(n)\)

然后这题还有若干种优化写法但我一个都没想到……

  • 关于 \(\varphi(\prod\limits_{i=l}^r a_i)\) 的多种求法:
    有一个优化暴力是对每个质数开set记录在哪些 \(a_i\) 中出现过了,求 \(\varphi\) 时枚举质数check其在这个范围内是否出现过以计算 \(\prod\frac{p_i-1}{p_i}\)
    另一个做法是(对于多次询问)使用带修莫队但是我不记得怎么写这个东西了
    还有一个做法是线段树+bitset统计每个质因子是否出现过,统计答案时用 s._Find_next() 遍历bitset
    以及一个复杂度最优的做法是离线下来CDQ分治
    image
    大意是对于每个查询,转化为求一个四分之一平面内的点权之积
    貌似强制在线的话还可以用KD-tree代替CDQ欸
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 400010
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
bool npri[N];
ll ans[N], inv[N], bit2[N], bit3[N];
set<int> s[N];
const ll mod=1e9+7;
int a[N], pri[N], pcnt, low[N], lowp[N], bit[N], op[N], top;
struct point{int x, y, t, rk; ll val, inv;}q[1800010];
inline void upd(int i, int dat) {for (; i<=n; i+=i&-i) bit[i]+=dat;}
inline int query(int l, int r) {
	int ans=0; --l;
	while (r>l) ans+=bit[r], r-=r&-r;
	while (l>r) ans-=bit[l], l-=l&-l;
	return ans;
}
inline void upd2(int i, ll val) {for (; i<=n; i+=i&-i) bit2[i]=bit2[i]*val%mod;}
inline ll query2(int i) {ll ans=1; for (; i; i-=i&-i) ans=ans*bit2[i]%mod; return ans;}
inline void upd3(int i, ll val) {for (; i<=n; i+=i&-i) bit3[i]=bit3[i]*val%mod;}
inline ll query3(int i) {ll ans=1; for (; i; i-=i&-i) ans=ans*bit3[i]%mod; return ans;}
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod,b>>=1) if (b&1) ans=ans*a%mod; return ans;}
void add(int i, int val) {
	for (int now=val,t; now>1; now/=lowp[now]) {
		t=low[now];
		auto it=s[t].lower_bound(i), x=it, y=it;
		assert(*it!=i);
		if (it!=s[t].begin()) ++top, q[top]={*(--x), i, top, 0, t*inv[t-1]%mod, (t-1)*inv[t]%mod};
		if (it!=s[t].end()) ++top, q[top]={i, *y, top, 0, t*inv[t-1]%mod, (t-1)*inv[t]%mod};
		if (it!=s[t].begin() && it!=s[t].end()) ++top, q[top]={*x, *y, top, 0, (t-1)*inv[t]%mod, t*inv[t-1]%mod};
		++top, q[top]={i, i, top, 0, (t-1)*inv[t]%mod, t*inv[t-1]%mod};
		s[t].insert(i);
	}
}
void del(int i, int val) {
	for (int now=val,t; now>1; now/=lowp[now]) {
		t=low[now];
		auto it=s[t].lower_bound(i), x=it, y=it;
		assert(*it==i);
		if (it!=s[t].begin()) ++top, q[top]={*(--x), i, top, 0, (t-1)*inv[t]%mod, t*inv[t-1]%mod};
		if ((++y)!=s[t].end()) ++top, q[top]={i, *y, top, 0, (t-1)*inv[t]%mod, t*inv[t-1]%mod};
		if (it!=s[t].begin() && y!=s[t].end()) ++top, q[top]={*x, *y, top, 0, t*inv[t-1]%mod, (t-1)*inv[t]%mod};
		++top, q[top]={i, i, top, 0, t*inv[t-1]%mod, (t-1)*inv[t]%mod};
		s[t].erase(it);
	}
}
int qphi(int n) {
	int ans=n;
	for (int i=1; i<=pcnt&&pri[i]*pri[i]<=n; ++i) if (ans%pri[i]==0) {
		ans=ans/pri[i]*(pri[i]-1);
		do {n/=pri[i];} while (n%pri[i]==0);
	}
	if (n>1) ans=ans/n*(n-1);
	return ans;
}

void cdq(int l, int r) {
	if (l==r) return ;
	int mid=(l+r)>>1;
	cdq(l, mid); cdq(mid+1, r);
	sort(q+l, q+mid+1, [](point a, point b){return a.x>b.x;});
	sort(q+mid+1, q+r+1, [](point a, point b){return a.x>b.x;});
	int pos1=l, pos2=mid+1;
	for (; pos2<=r; ++pos2) {
		for (; pos1<=mid && q[pos1].x>=q[pos2].x; ++pos1) if (!q[pos1].rk) upd2(q[pos1].y, q[pos1].val);
		if (q[pos2].rk) ans[q[pos2].rk]=ans[q[pos2].rk]*query2(q[pos2].y)%mod;
	}
	for (int i=l; i<pos1; ++i) if (!q[i].rk) upd2(q[i].y, q[i].inv);
	// sort(q+l, q+r+1, [](point a, point b){return a.t<b.t;});
}

signed main()
{
	freopen("phi.in", "r", stdin);
	freopen("phi.out", "w", stdout);

	n=read(); m=read();
	inv[0]=inv[1]=1;
	for (int i=0; i<N; ++i) bit2[i]=bit3[i]=1;
	for (int i=2; i<N; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	for (int i=2; i<N; ++i) {
		if (!npri[i]) pri[++pcnt]=low[i]=lowp[i]=i;
		for (int j=1,x; j<=pcnt&&i*pri[j]<N; ++j) {
			npri[x=i*pri[j]]=1;
			if (!(i%pri[j])) {
				low[x]=pri[j];
				lowp[x]=lowp[i]*pri[j];
				break;
			}
			else low[x]=lowp[x]=pri[j];
		}
	}
	for (int i=1; i<=n; ++i) upd(i, a[i]=read()), add(i, a[i]), upd3(i, a[i]);
	for (int i=1,l,r; i<=m; ++i) {
		op[i]=read(); l=read(); r=read();
		if (op[i]==0) {
			del(l, a[l]), add(l, r);
			upd3(l, inv[a[l]]), upd3(l, r);
			upd(l, -a[l]), upd(l, a[l]=r);
		}
		else if (op[i]==1) ans[i]=qphi(query(l, r));
		else {
			++top, q[top]={l, r, top, i, 0, 0};
			ans[i]=query3(r)%mod*qpow(query3(l-1), mod-2)%mod;
		}
	}
	// cout<<"q: "<<endl; for (int i=1; i<=top; ++i) cout<<"("<<q[i].x<<','<<q[i].y<<','<<q[i].rk<<','<<q[i].val<<")"<<endl;
	// cerr<<"top: "<<top<<endl;
	cdq(1, top);
	// for (int i=1; i<=top; ++i) if (q[i].rk) {
	// 	for (int j=1; j<i; ++j) if (!q[j].rk && q[j].x>=q[i].x && q[j].y<=q[i].y) {
	// 		ans[q[i].rk]=(ans[q[i].rk]*q[j].val)%mod;
	// 	}
	// }
	// cout<<query2(2)<<endl;
	for (int i=1; i<=m; ++i) if (op[i]) printf("%lld\n", (ans[i]%mod+mod)%mod);
	// for (int i=1; i<=top; ++i) if (!q[i].rk) assert(q[i].val*q[i].inv%mod==1);

	return 0;
}
posted @ 2022-01-13 19:22  Administrator-09  阅读(2)  评论(0编辑  收藏  举报