CodeForces 1114F Please, another Queries on Array?(线段树 + 状态压缩 + 数论)

 

 

大致题意:给你一长度为n的序列,和q个操作。一个是区间乘以某个数字,另一个是查询区间积的欧拉函数对1e9+7取模的结果。出了n和q,这里所有出现的数字都是小于300的。

这里如果直接做,显然是不行的,因为欧拉函数虽然具有积性,但是并不能够取模,意味着不能直接保存区间积。同时,欧拉函数不具有完全积性,当gcd不为1的时候,不是直接相乘。但是这里有一个很重要的条件,即出现的每个数字都是小于300的,而300以内的质数恰好有62个,而2^62恰好可以用long long存下,这也就暗示了我们可以用状态压缩的方式,存储每个质数是否作为质因子出现。

根据欧拉函数的性质φ(p^x)=(p-1)p^(x-1),为了方便,我们不妨令φ'(p^x)=p^x,然后在线段树中每次计算区间的φ'和用LL状压表示点区间积的质因子。在合并的时候,状态直接用按位或,而φ'则是直接相乘取模即可。对于每一个询问,我们在线段树中求出φ',然后再通过乘以每一个质因子的逆元和质因子减一,把φ'变成φ之后输出。具体见代码:

#include <bits/stdc++.h>
#define INF 1e18
#define fi first
#define se second
#define LL long long
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;

const int N = 400010;
const int mod = 1e9 + 7;

int b[N],p[N],phi[N],inv[N],n,q,sz;
bool isp[N]; char s[100];
LL a[N],pw[N];

pair<LL,LL> c=make_pair(0LL,1LL);

inline LL qpow(LL x,LL n)
{
    LL res=1;
    while(n)
    {
        if (n&1) res=res*x%mod;
        x=x*x%mod; n>>=1;
    }
    return res;
}

struct ST
{
    #define ls i<<1
    #define rs i<<1|1

	struct node
	{
	    pair<LL,LL> phi,lazy;
	    int l,r;
    } T[N<<2];

    inline void push_up(int i)
    {
        T[i].phi.fi=T[ls].phi.fi|T[rs].phi.fi;
        T[i].phi.se=T[ls].phi.se*T[rs].phi.se%mod;
    }

    inline void upd(pair<LL,LL> &a,pair<LL,LL> b)
    {
        a.fi|=b.fi;
	    a.se=a.se*b.se%mod;
    }

	void build(int i,int l,int r)
	{
		T[i]=node{c,c,l,r};
		if (l==r)
		{
			T[i].phi=make_pair(a[l],b[l]);
			return;
		}
		int mid=(l+r)>>1;
		build(ls,l,mid);
		build(rs,mid+1,r);
		push_up(i);
	}

	inline void push_down(int i)
	{
	    pair<LL,LL> tmp=T[i].lazy;
	    upd(T[ls].lazy,T[i].lazy);
        upd(T[rs].lazy,T[i].lazy);
        int sz=T[ls].r-T[ls].l+1;
        tmp.se=qpow(T[i].lazy.se,sz);
	    upd(T[ls].phi,tmp);
	    sz=T[rs].r-T[rs].l+1;
	    tmp.se=qpow(T[i].lazy.se,sz);
        upd(T[rs].phi,tmp);
		T[i].lazy=c;
	}

	void update(int i,int l,int r,pair<LL,LL> x)
	{
		if (T[i].l==l&&T[i].r==r)
		{
		    upd(T[i].lazy,x);
		    x.se=qpow(x.se,r-l+1);
			upd(T[i].phi,x); return;
		}
		if (T[i].lazy!=c) push_down(i);
		int mid=(T[i].l+T[i].r)>>1;
		if (mid>=r) update(ls,l,r,x);
		else if (mid<l) update(rs,l,r,x);
		else
		{
			update(ls,l,mid,x);
			update(rs,mid+1,r,x);
		}
		push_up(i);
	}

	pair<LL,LL> getphi(int i,int l,int r)
	{
		if (T[i].l==l&&T[i].r==r) return T[i].phi;
		if (T[i].lazy!=c) push_down(i);
		int mid=(T[i].l+T[i].r)>>1;
		if (mid>=r) return getphi(ls,l,r);
		else if (mid<l) return getphi(rs,l,r);
		else
        {
            pair<LL,LL> a,b;
            a=getphi(ls,l,mid);
            b=getphi(rs,mid+1,r);
            upd(a,b); return a;
        }
	}

} seg;

void init()
{
    sz=0; phi[1]=1;
    for(int i=0;i<62;i++)
        pw[i]=1LL<<i;
    for(int i=2;i<301;i++)
    {
        if(!isp[i])p[++sz]=i,phi[i]=i-1;
         for(int j=1;j<=sz&&(LL)i*p[j]<N;j++)
         {
            isp[i*p[j]]=1;
            if(i%p[j]==0)
            {
                phi[p[j]*i]=phi[i]*p[j];
                break;
            } else phi[p[j]*i]=(p[j]-1)*phi[i];
        }
    }
    inv[0]=inv[1]=1;
    for(int i=2;i<301;i++)
        inv[i]=(mod-mod/i)*(LL)inv[mod%i]%mod;
}

int main()
{
    init(); scc(n,q);
    for(int i=1;i<=n;i++)
    {
        int x,xx; sc(x); xx=x;
        for(int j=1;j<=sz&&p[j]<=x;j++)
            while (x%p[j]==0) x/=p[j],a[i]|=pw[j-1];
        b[i]=xx;
    }
    seg.build(1,1,n);
    while(q--)
    {
        scanf("%s",s);
        if (s[0]=='T')
        {
            int x,y; scc(x,y);
            pair<LL,LL> tmp=seg.getphi(1,x,y);
            LL res=tmp.se;
            for(int i=1;i<=62;i++)
                if (tmp.fi&(1LL<<(i-1))) res=res*inv[p[i]]%mod*(p[i]-1)%mod;
            printf("%lld\n",res);
        } else
        {
            LL tmp=0;
            int x,y,z,zz; sccc(x,y,z); zz=z;
            for(int i=1;i<=sz&&p[i]<=z;i++)
                while (z%p[i]==0) z/=p[i],tmp|=pw[i-1];
            seg.update(1,x,y,make_pair(tmp,zz));
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值