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
#include
#include
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;j300)break;
			ok[k]=1;
			if(i%prime[j]==0)break;
		}
	}
	for(ll i=2;i<=300;++i)
	{
		ll tmp=i;
		for(int j=0;jtmp)break;
			if(tmp%prime[j]==0)
			{
				mask[i]|=(1ll<1)mask[i]|=(1ll<

 

你可能感兴趣的:(线段树(权值线段树)/树状数组)