两道线段树例题

113 阅读3分钟

CF1114F Please, another Queries on Array?

思路

ψ(x)=xp=p1pnp1p=xp=p1pn(p1)inv(p)\psi(x)=x\prod\limits_{p=p_1}^{p_n}\frac{p-1}{p}=x\prod\limits_{p=p_1}^{p_n}(p-1)\cdot inv(p)发现,对于区间[l,r][l,r]的询问,只需要维护出区间乘积,该乘积的质因数有哪些,就可以算出答案。区间乘积很好维护。

考虑如何求这个乘积包含的质因数,题目中a[i],x300a[i],x\leq 300,而300300以内的质数只有6262个,所以可以采取类似状态压缩的思想,用一个long longlong\ long类型的数字记录某个区间的乘积包含了哪些质因数,合并两个区间时直接把这两个状态按位或起来即可。带修改操作,所以放到线段树上做。

代码

#include<bits/stdc++.h>
#define lc p<<1
#define rc p<<1|1
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define en puts("")
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll> 
typedef long long ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
void read() {}
void OP() {}
void op() {}
template <typename T, typename... T2>
inline void read(T &_, T2 &... oth)
{
    int __=0;
    _=0;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            __=1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        _=_*10+ch-48;
        ch=getchar();
    }
    _=__?-_:_;
    read(oth...);
}
template <typename T>
void Out(T _)
{
    if(_<0)
    {
        putchar('-');
        _=-_;
    }
    if(_>=10)
       Out(_/10);
    putchar(_%10+'0');
}
template <typename T, typename... T2>
inline void OP(T _, T2... oth)
{
	Out(_);
	putchar('\n');
	OP(oth...);
}
template <typename T, typename... T2>
inline void op(T _, T2... oth)
{
	Out(_);
	putchar(' ');
	op(oth...);
}
/*#################################*/
const ll N=4E5+10,P=1E9+7;
ll n,q;
ll he[N],one[N],a[N],t[4*N],tag[4*N],otag[4*N],sta[4*N],f[100];
vector<ll> pri;
struct Node
{
	ll val,state;
};
ll ksm(ll x,ll y)
{
	ll ret=1;
	x%=P;
	while(y)
	{
		if(y&1)
			ret=ret*x%P;
		x=x*x%P;
		y>>=1;
	}
	return ret;
}
void sieve()
{
	rep(i,2,300)
	{
		if(!he[i])
		{
			he[i]=1;
			pri.emplace_back(i);
		}
		for(auto k:pri)
		{
			if(k*i>300)
				break;
			he[i*k]=1;
			if(i%k==0)
				break;
		}
	}
}
ll find(ll x)
{
	ll ret=0;
	rep(i,0,pri.size()-1)
	{
		ll k=pri[i];
		if(x%k==0)
		{
			ret|=1ll<<i;
			while(x%k==0)
				x/=k;
		}
	}
	return ret;
}
void update(ll p)
{
	t[p]=t[lc]*t[rc]%P;
	sta[p]=sta[lc]|sta[rc];
}
void build(ll p,ll l,ll r)
{
	tag[p]=1;
	if(l==r)
	{
		sta[p]=one[a[l]];
		t[p]=a[l];
		return;
	}
	ll mid=(l+r)>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	update(p);
}
void flag(ll p,ll len,ll val,ll o)
{
	t[p]=t[p]*ksm(val,len)%P;
	tag[p]=tag[p]*val%P;
	sta[p]|=o;
	otag[p]|=o;
}
void push_down(ll p,ll l,ll r)
{
	ll mid=(l+r)>>1;
	flag(lc,mid-l+1,tag[p],otag[p]);
	flag(rc,r-mid,tag[p],otag[p]);
	tag[p]=1;
	otag[p]=0;
}
void modify(ll p,ll l,ll r,ll al,ll ar,ll val)
{
	if(al<=l && ar>=r)
	{
		flag(p,r-l+1,val,one[val]);
		return;
	}
	ll mid=(l+r)>>1;
	if(otag[p])
		push_down(p,l,r);
	if(al<=mid)
		modify(lc,l,mid,al,ar,val);
	if(ar>mid)
		modify(rc,mid+1,r,al,ar,val);
	update(p);
}
Node merge(Node u,Node v)
{
	u.val=u.val*v.val%P;
	u.state|=v.state;
	return u;
}
Node query(ll p,ll l,ll r,ll al,ll ar)
{
	if(al<=l && ar>=r)
		return {t[p],sta[p]};
	ll mid=(l+r)>>1;
	if(otag[p])
		push_down(p,l,r);
	if(ar<=mid)
		return query(lc,l,mid,al,ar);
	if(al>mid)
		return query(rc,mid+1,r,al,ar);
	return merge(query(lc,l,mid,al,ar),query(rc,mid+1,r,al,ar));
}
void print()
{
	rep(i,1,4*n)
	{
		op(i,t[i]);
		en;
	}
	en;
}
int main()
{
	sieve();
	rep(i,0,61)
		f[i]=(pri[i]-1)*ksm(pri[i],P-2)%P;
	read(n,q);
	rep(i,1,n)
		read(a[i]);
	rep(i,1,300)
		one[i]=find(i);
	build(1,1,n);
	string ty;
	ll l,r,x;
	while(q--)
	{
		cin>>ty;
		read(l,r);
		if(ty[0]=='T')
		{
			Node res=query(1,1,n,l,r);
			ll ans=res.val;
			rep(i,0,61)
				if((1ll<<i)&res.state)
					ans=ans*f[i]%P;
			OP(ans);
		}
		else
		{
			read(x);
			modify(1,1,n,l,r,x);
		}
	}
}

[TJOI2018]数学计算

思路

刚开始想每次给xx乘上数字,除法运算时乘逆元就行,但是MM并不一定是质数(当且仅当a,ba,b互质时,aa对模数bb存在逆元)。

a[i]a[i]为第ii次操作时给xx乘的数字,初始时全为11,那么所有a[i]a[i]的乘积就是当前xx的值(类似区间和),操作二就是将a[pos]a[pos]归为11,容易想到用线段树维护一下,比较像上海市赛的签到题,但是那道题没有修改操作,只需要维护下前缀乘积即可。

代码

#include<bits/stdc++.h>
#define lc p<<1
#define rc p<<1|1
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define en puts("")
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll> 
typedef long long ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
void read() {}
void OP() {}
void op() {}
template <typename T, typename... T2>
inline void read(T &_, T2 &... oth)
{
    int __=0;
    _=0;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            __=1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        _=_*10+ch-48;
        ch=getchar();
    }
    _=__?-_:_;
    read(oth...);
}
template <typename T>
void Out(T _)
{
    if(_<0)
    {
        putchar('-');
        _=-_;
    }
    if(_>=10)
       Out(_/10);
    putchar(_%10+'0');
}
template <typename T, typename... T2>
inline void OP(T _, T2... oth)
{
	Out(_);
	putchar('\n');
	OP(oth...);
}
template <typename T, typename... T2>
inline void op(T _, T2... oth)
{
	Out(_);
	putchar(' ');
	op(oth...);
}
/*#################################*/
const ll N=1E5+10;
ll n,P;
ll ans[4*N];
vector<ll> ve;
void update(ll p)
{
	ans[p]=ans[lc]*ans[rc]%P;
}
void build(ll p,ll l,ll r)
{
	if(l==r)
	{
		ans[p]=1;
		return;
	}
	ll mid=(l+r)>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	update(p);
}
void modify(ll p,ll l,ll r,ll pos,ll val)
{
	if(l==r)
	{
		ans[p]=val>0?val%P:1;
		return;
	}
	ll mid=(l+r)>>1;
	if(pos<=mid)
		modify(lc,l,mid,pos,val);
	else
		modify(rc,mid+1,r,pos,val);
	update(p);
}
void solve()
{
	read(n,P);
	build(1,1,n);
	ll ty,x;
	rep(i,1,n)
	{
		read(ty,x);
		if(ty==1)
			modify(1,1,n,i,x);
		else
			modify(1,1,n,x,-ve[x-1]);
		ve.emplace_back(x);
		OP(ans[1]);
	}
}
int main()
{
	int _;
	cin>>_;
	while(_--)
	{
		solve();
	}
}