codeforces 1114F Please, another Queries on Array?(数学+线段树)

qwq被Creed吊锤

sro Creed orz

一道好题。
首先考虑该如何求 φ ( i ) \varphi(i) φ(i),由于这个数据范围,尤其是权值的范围,就让人感觉可能存在一些突破的机会

由于欧拉函数是一个积性函数,那么如果存在两个互质的两个数 a 和 b a和b ab φ ( a ∗ b ) = φ ( a ) × φ ( b ) \varphi(a*b) = \varphi(a) \times \varphi(b) φ(ab)=φ(a)×φ(b)

由于质数的欧拉函数值比较特别,并且值域的范围也是比较小,不妨

考虑对于一个质数 p p p k k k次方,应该如何计算他的欧拉函数值

那么我们比较容易发现,只有 p p p的倍数才不是互质的,那么我们不让用总的数的个数,减去 1 到 p k 1到p^k 1pk p p p的倍数,也就说说 φ ( p k ) = p k − p k − 1 = p k − 1 × ( p − 1 ) = p k × p − 1 p \varphi(p^k) = p^k - p^{k-1} = p^{k-1} \times (p-1) = p^k \times \frac{p-1}{p} φ(pk)=pkpk1=pk1×(p1)=pk×pp1

那么由于积性函数,我们可以算出来每一个质因子的对应的欧拉函数的值,然后直接相乘,前一项的乘积就是所有 a i a_i ai的乘积。

后面一项,我们发现质数的数量并不是很多,而且对于一个质因子,我们只需要关心他是不是存在,我们可以直接把状态压缩成一个 l o n g l o n g long long longlong来表示。

那么维护一个区间修改,区间询问的值,可以直接用线段树来实现

通过一些奇奇怪怪的方法 u p up up

void up(int root)
{
	f[root].prob = f[2*root].prob*f[2*root+1].prob%mod;
	f[root].ymh = f[2*root].ymh | f[2*root+1].ymh;
}

有一个需要注意的细节,就是我们进行乘法的时候,下传标记的时候呢,对于一个区间来说,我们要乘一个类似于乘积的形式。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
#define int long long
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 1e6+1e2;
const int mod = 1e9+7;
struct Node{
  int prob;
  int ymh;
};
Node f[4*maxn];
int n,m;
int prime[maxn];
int ch[4*maxn];
int tag[4*maxn];
int a[maxn];
int val[maxn];
int qsm(int i,int j)
{
	int ans=1;
	while (j)
	{
		if (j&1) ans=ans*i%mod;
		i=i*i%mod;
		j>>=1;
	}
	return ans;
}
void up(int root)
{
	f[root].prob = f[2*root].prob*f[2*root+1].prob%mod;
	f[root].ymh = f[2*root].ymh | f[2*root+1].ymh;
}
void pushdown(int root,int l,int r)
{
	if (ch[root]!=1)
	{
		ch[2*root]=ch[2*root]*ch[root]%mod;
		ch[2*root+1]=ch[2*root+1]*ch[root]%mod; 
		int mid = l+r >> 1;
		f[2*root].prob=f[2*root].prob*qsm(ch[root],mid-l+1)%mod;
		f[2*root+1].prob=f[2*root+1].prob*qsm(ch[root],r-(mid+1)+1)%mod;
		ch[root]=1; 
	}
	if (tag[root])
	{
		tag[2*root]|=tag[root];
		tag[2*root+1]|=tag[root];
		f[2*root].ymh|=tag[root];
		f[2*root+1].ymh|=tag[root];
		tag[root]=0;
	}
}
void build(int root,int l,int r)
{
      ch[root]=1;
	tag[root]=0;
	if (l==r)
	{
		f[root].prob = a[l];
		f[root].ymh = val[l];
		return;
	} 
	int mid = l+r >> 1;
	build(2*root,l,mid);
	build(2*root+1,mid+1,r);
	up(root);
}
void update(int root,int l,int r,int x,int y,int p)
{
	
	if (x<=l && r<=y)
	{
		p%=mod;
		f[root].prob = f[root].prob*qsm(p,r-l+1)%mod;
		ch[root]=ch[root]*p%mod;
		return;
	}
	pushdown(root,l,r);
	int mid = l+r >> 1;
	if (x<=mid) update(2*root,l,mid,x,y,p);
	if (y>mid) update(2*root+1,mid+1,r,x,y,p);
	up(root);
}
void modify(int root,int l,int r,int x,int y,int p)
{
	if (x<=l && r<=y)
	{
		f[root].ymh|=p;
		tag[root]|=p;
		return;
	}
	pushdown(root,l,r);
	int mid = l+r >> 1;
	if (x<=mid) modify(2*root,l,mid,x,y,p);
	if (y>mid) modify(2*root+1,mid+1,r,x,y,p);
	up(root);
}
Node query(int root,int l,int r,int x,int y)
{
	if (x<=l && r<=y) return f[root];
	pushdown(root,l,r);
	int mid = l+r >> 1;
	Node ans,ans1,ans2;
	ans=ans1=ans2=(Node){1,0};
	if (x<=mid) ans1 = query(2*root,l,mid,x,y);
	if (y>mid) ans2 = query(2*root+1,mid+1,r,x,y);
	ans.prob = ans1.prob*ans2.prob%mod;
	ans.ymh = ans1.ymh | ans2.ymh;
	return ans;
}
int q;
bool check(int x)
{
	for (int i=2;i*i<=x;i++)
	{
		if (x%i==0) return false;
	}
	return true;
}
int tot;
void init()
{
	for (int i=2;i<=300;i++) 
	  if (check(i)) 
	    prime[++tot]=i;
}
int szh[maxn],inv[maxn];
signed main()
{
  n=read(),q=read();
  init();
  for (int i=1;i<=n;i++) a[i]=read();
  for (int i=1;i<=300;i++)
  {
  	 for (int j=1;j<=tot;j++)
  	 {
  	 	if (i%prime[j]==0) szh[i]|=(1ll <<(j-1));
	 }
  }
  for (int i=1;i<=n;i++) val[i]=szh[a[i]]; 
  for (int i=1;i<=300;i++) inv[i]=qsm(i,mod-2);
  build(1,1,n);
  for (int i=1;i<=q;i++)
  {
  	 char s[20];
  	 scanf("%s",s+1);
  	 if (s[1]=='M')
  	 {
  	 	int l=read(),r=read(),x=read();
  	 	update(1,1,n,l,r,x);
  	 	modify(1,1,n,l,r,szh[x]);
	 }
	 else
	 {
	 	int l=read(),r=read();
	 	Node now = query(1,1,n,l,r);
	 	int ans=now.prob;
	 	for (int j=1;j<=tot;j++)
	 	{
	 		if ((1ll<<(j-1)) & now.ymh)
	 		{
	 			ans=ans*(prime[j]-1)%mod*inv[prime[j]]%mod;
			}
		}
	      cout<<ans<<"\n";
	 }
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值