【YbtOJ#911】欧拉函数

题目

题目链接:https://www.ybtoj.com.cn/contest/125/problem/1

\(n\leq 50000;Q\leq 10^5;a_i,x\leq 40000\),除了操作编号,其余数据全部随机。时限 \(3.5s\)

思路

数据随机,考虑乱搞。
首先需要一个线段树维护区间和以及区间乘积 \(\bmod 10^9+7\)


对于操作一,区间和 \(\leq 2\times 10^9\),考虑 洛谷P5071 [Ynoi2015] 此时此刻的光辉 的套路,因为 \(1300^3>2\times 10^9\),我们直接暴力求出这个数 \(1300\) 内的质因子,然后剩余最多两个超过 \(1300\) 的质因子,用 Pollard-Rho 求出其中一个,另外一个除一下就出来了。
这部分的单次复杂度为 \(O(\log n+1300+\text{PR 玄学复杂度})\)。忽略掉 PR 的复杂度以及常数的话,极限大约需要执行 131,600,000 次。


对于操作二,因为是区间乘积,所以保证了这个数的所有质因子都是不超过 \(40000\) 的。考虑直接暴力求出所有的质因子。
在线段树上每一个节点维护一个 bitset,表示这个区间的数的质因子集合。然后每次直接把 \([l,r]\) 的 bitset 暴力合并。
接下来直接无脑暴力枚举所有不超过 \(40000\) 的质数(\(4202\) 个),然后更新答案即可。
这部分单词时间复杂度为 \(O(\log n\log 40000+4202)\),忽略常数极限大约需要执行 445,800,000 次。

但是由于这道题数据随机,所以实际上常数非常小。可以通过。
正解是复杂度 \(O((n+Q)k\log^2 n)\) 的二维数点,其中 \(k\) 是一个数不同的质因子数量。

代码

怎么看我这个都比正解长。。。

#include <bits/stdc++.h>
#define reg register
using namespace std;
typedef long long ll;

const int N=50010,M=4206,Lim=2000,MOD=1e9+7;
const int prime[3]={2,7,61};
int n,m,Q,maxd,a[N],prm[N],inv[N],id[N];
bool v[N];
bitset<M> g,f[N];

int gcd(int x,int y)
{
	return y?gcd(y,x%y):x;
}

int fpow(int x,int k,int mod=MOD)
{
	int ans=1;
	for (;k;k>>=1,x=1LL*x*x%mod)
		if (k&1) ans=1LL*ans*x%mod;
	return ans;
}

void findprm(int n)
{
	for (reg int i=2;i<=n;i++)
		if (!v[i])
		{
			prm[++m]=i; id[i]=m;
			inv[m]=fpow(i,MOD-2)%MOD;
			for (reg int j=1;j*i<=n;j++)
				v[j*i]=1;
		}
}

void getfac(int n)
{
	for (reg int i=1;i<=n;i++)
	{
		int p=i;
		for (reg int j=1;prm[j]*prm[j]<=p;j++)
			if (!(p%prm[j]))
			{
				f[i][j]=1;
				while (!(p%prm[j])) p/=prm[j];
			}
		if (p>1) f[i][id[p]]=1;
	}
}

struct SegTree
{
	int sum[N*4],mul[N*4];
	bitset<M> d[N*4];
	
	void pushup(int x)
	{
		d[x]=d[x*2]|d[x*2+1];
		sum[x]=sum[x*2]+sum[x*2+1];
		mul[x]=1LL*mul[x*2]*mul[x*2+1]%MOD;
	}
	
	void update(int x,int l,int r,int k,int v)
	{
		if (l==r)
		{
			d[x].reset(); d[x]|=f[v];
			sum[x]=mul[x]=v;
			return;
		}
		int mid=(l+r)>>1;
		if (k<=mid) update(x*2,l,mid,k,v);
			else update(x*2+1,mid+1,r,k,v);
		pushup(x);
	}
	
	int query1(int x,int l,int r,int ql,int qr)
	{
		if (ql<=l && qr>=r) return sum[x];
		int mid=(l+r)>>1,res=0;
		if (ql<=mid) res+=query1(x*2,l,mid,ql,qr);
		if (qr>mid) res+=query1(x*2+1,mid+1,r,ql,qr);
		return res;
	}
	
	int query2(int x,int l,int r,int ql,int qr)
	{
		if (ql<=l && qr>=r)
		{
			g|=d[x];
			return mul[x];
		}
		int mid=(l+r)>>1,res=1;
		if (ql<=mid) res=1LL*res*query2(x*2,l,mid,ql,qr)%MOD;
		if (qr>mid) res=1LL*res*query2(x*2+1,mid+1,r,ql,qr)%MOD;
		return res;
	}
}seg;

bool MR(int n)
{
	for (reg int i=0;i<=2;i++)
	{
		if (n==prime[i]) return 1;
		int p=n-1,pw=fpow(prime[i],p,n);
		while (pw==1 && !(p&1))
			p>>=1,pw=fpow(prime[i],p,n);
		if (pw!=1 && pw!=n-1) return 0;
	}
	return 1;
}

int work(int n)
{
	int s=0,t=0,c=rand()%(n-1)+1,val=1,lim=2;
	for (reg int i=1;;i++)
	{
		t=(1LL*t*t+c)%n; val=1LL*val*abs(s-t)%n;
		if (i==lim || !(i%127))
		{
			int d=gcd(n,val);
			if (d>1) return d;
			if (i==lim) s=t,val=1,lim<<=1;
		}
	}
	return 19260817;
}

void PR(int n)
{
	if (n<maxd || n<2) return;
	if (MR(n)) { maxd=n; return; }
	int p=work(n);
	while (p>=n) p=work(n);
	while (!(n%p)) n/=p;
	PR(n); PR(p);
}

int main()
{
	freopen("phi.in","r",stdin);
	freopen("phi.out","w",stdout);
	srand('Q'+'u'+'a'+'n'+'t'+'A'+'s'+'k'+'A'+'K'+'I'+'O'+'I');
	findprm(40000);
	getfac(40000);
	scanf("%d%d",&n,&Q);
	for (reg int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		seg.update(1,1,n,i,a[i]);
	}
	while (Q--)
	{
		int opt,l,r;
		scanf("%d%d%d",&opt,&l,&r);
		if (opt==0)
		{
			seg.update(1,1,n,l,r);
			a[l]=r;
		}
		if (opt==1)
		{
			int sum=seg.query1(1,1,n,l,r),phi=sum;
			for (reg int i=1;prm[i]<=1300;i++)
				if (!(sum%prm[i]))
				{
					phi=phi/prm[i]*(prm[i]-1);
					while (!(sum%prm[i])) sum/=prm[i];
				}
			if (sum>1)
			{
				maxd=0; PR(sum);
				phi=phi/maxd*(maxd-1);
				if (1LL*maxd*maxd!=sum && maxd!=sum)
					phi=phi/(sum/maxd)*(sum/maxd-1);
			}
			printf("%d\n",phi);
		}
		if (opt==2)
		{
			g.reset();
			int mul=seg.query2(1,1,n,l,r);
			for (reg int i=1;i<M;i++)
				if (g[i]) mul=1LL*mul*inv[i]%MOD*(prm[i]-1)%MOD;
			printf("%d\n",mul);
		}
	}
	return 0;
}
posted @ 2021-02-26 17:15  stoorz  阅读(109)  评论(0编辑  收藏  举报