Codeforces Round #538 (Div. 2) F - Please, another Queries on Array?(线段树+位运算维护集合)

题目

给一一个n<=4e5的序列a[],ai<=300

两种操作,操作数q<=2e5

一种是区间[l,r]乘上一个值x 

另一种是询问区间[l,r]乘积的欧拉函数值

思路来源

各路cf神仙代码

 

题解1

维护区间积和区间或,

先把300以内的质数筛出来,然后每个数质因数分解

每个数有哪些质数对应用位运算压位,bitset也可以

这样两个数相乘的时候,它们乘积的质因子就是二者的并集

求欧拉函数\varphi (x)的时候,先求区间积,

然后看质因子有哪些,

压位的第i位为1说明有prime[i],乘以prime[i]/(prime[i]-1)即可

由于是mod意义下的运算,再先搞一下逆元

代码1

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=(1<<20)+5;
const int maxm=4e5+5;
const int maxv=310;
bool ok[maxv];
ll prime[maxv],id[maxv],cnt;
ll inv[maxv],mask[maxv];
int n,q;
ll a[maxm];
char op[20]; 
ll modpow(ll x,ll n,ll mod)
{
	if(n==0)return 1;
	ll p=modpow(x,n/2,mod),res=p*p%mod;
	if(n&1)(res*=x)%=mod;
	return res;
}
struct dat
{
	ll mul,mask;
	dat():mul(1),mask(0){}
	dat(ll a,ll b):mul(a),mask(b){}
	dat operator+(const dat &rhs)const
	{
		return dat((mul*rhs.mul)%mod,mask|rhs.mask);
	}
}e[maxn],lazy[maxn];
void pushup(int p)
{
	e[p]=e[p<<1]+e[p<<1|1];
}
void build(int p,int l,int r)
{
	if(l==r)
	{
		e[p]=dat(a[l],mask[a[l]]);
		return;
	}
	int mid=(l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	pushup(p);
}
void app(int p,int l,int r,dat v)
{
	lazy[p]=lazy[p]+v;
	v.mul=modpow(v.mul,(r-l+1)%(mod-1),mod);//费马
	e[p]=e[p]+v; 
}
void pushdown(int p,int l,int r)
{
	int mid=(l+r)>>1;
	app(p<<1,l,mid,lazy[p]);
	app(p<<1|1,mid+1,r,lazy[p]);
	lazy[p]=dat(1,0);
}
void update(int p,int l,int r,int ql,int qr,dat v)
{
	if(ql<=l&&r<=qr)
	{
		app(p,l,r,v);
		return;
	}
	int mid=(l+r)>>1;
	pushdown(p,l,r);
	if(ql<=mid)update(p<<1,l,mid,ql,qr,v);
	if(qr>mid)update(p<<1|1,mid+1,r,ql,qr,v);
	pushup(p);
}
dat query(int p,int l,int r,int ql,int qr)
{
	if(ql<=l&&r<=qr)return e[p];
	int mid=(l+r)>>1;
	pushdown(p,l,r);
	return (ql<=mid?query(p<<1,l,mid,ql,qr):dat(1,0))+(qr>mid?query(p<<1|1,mid+1,r,ql,qr):dat(1,0)); 
}
void init()
{
	for(ll i=2;i<=300;++i)
	{
		if(!ok[i])
		{
		 prime[cnt]=i;
		 id[i]=cnt++;
	    }
		for(ll j=0;j<cnt;++j)
		{
			ll k=i*prime[j];
			if(k>300)break;
			ok[k]=1;
			if(i%prime[j]==0)break;
		}
	}
	for(ll i=2;i<=300;++i)
	{
		ll tmp=i;
		for(int j=0;j<cnt;++j)
		{
			if(prime[j]*prime[j]>tmp)break;
			if(tmp%prime[j]==0)
			{
				mask[i]|=(1ll<<id[prime[j]]);
				while(tmp%prime[j]==0)
				tmp/=prime[j];
			}
		}
		if(tmp>1)mask[i]|=(1ll<<id[tmp]);
	} 
	inv[1]=1;
	for(ll i=2;i<=300;++i)
	inv[i]=modpow(i,mod-2,mod);
}
int main()
{
	init();
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;++i)
	scanf("%I64d",a+i);
	build(1,1,n);
	while(q--)
	{
		int l,r,v;
		scanf("%s%d%d",op,&l,&r);
		if(op[0]=='M')
		{
			scanf("%d",&v);
			update(1,1,n,l,r,dat(v,mask[v]));
		}
		else 
		{
			dat ans=query(1,1,n,l,r);
			for(int i=0;i<cnt;++i)
			{
				if(ans.mask&(1ll<<i))
				(ans.mul*=(prime[i]-1)*inv[prime[i]]%mod)%=mod;
			}
			printf("%I64d\n",ans.mul);
		}
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值