码迷,mamicode.com
首页 > 其他好文 > 详细

CodeForces 1114F--Please, another Queries on Array?(欧拉函数+线段树)

时间:2020-07-12 19:09:23      阅读:52      评论:0      收藏:0      [点我收藏+]

标签:一个   onclick   pac   开始   lap   using   src   ase   筛选素数   

题目链接:https://codeforces.com/problemset/problem/1114/F

题目大意:给你n个数,q次操作,类型1,将区间[l,r]中每个数乘以x,类型2,询问$\varphi (\prod_{i=l}^{r}a_i)$。其中初始的n个数每个小于等于300,x<=300。

Example

Input
4 4
5 9 1 2
TOTIENT 3 3
TOTIENT 3 4
MULTIPLY 4 4 3
TOTIENT 4 4
Output
1
1
2

emmmm,线段树肯定要用上,接下来就是对于欧拉函数的性质的运用了,对于欧拉函数$\varphi (x)$来讲,如果$x$可以表示为两个互质的数的乘积即$x=a\times b$,那么$\varphi (x)=\varphi (a)\times \varphi (b)$。如果$x$可以表示为一个质数的幂的形式即$x=p^k$,$p$为质数。那么$\varphi (p^k)=p^k-p^{k-1}=(1-p)p^{k-1}$。

那么也就是说我们可以将每个数进行质因子分解,那么则有

$sum=\varphi (p_1^{k_1}\times p_2^{k2}\times p_3^{k_3}...)=\varphi(p_1^{k_1-1})\times \varphi(p_2^{k_2-1})\times...$

$sum=(p_1-1)p_1^{k_1-1}\times (p_2-1)p_2^{k_2-1}\times (p_3-1)p_3^{k_3-1}...$

那么也就是我们只需要维护一下$p_i-1$和$p_i^{k_i-1}$就可以了,按照我自己的超级复杂的写法就是先把每个数分解了,然后在300内筛选素数,总共62个,然后用状压来表示这些数是否存在,区间融合的时候需要对左儿子和右儿子的状态进行判断,是否存在出现了相同的素数,如果出现了,我们就对维护的$p_i^{k_i-1}$里面再乘上该素数。总的来说有点复杂,时间上是$O(62qlogn)$。。。直接T13了。。。。以下是刚开始的复杂写法:

技术图片
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;

#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define lc rt<<1
#define rc rt<<1|1
typedef long long ll;
const int mac=4e5+10;
const ll mod=1e9+7;

int a[mac],vis[350],prime[100],cnt=0;
ll yes_stk=0;
struct node
{
    int l,r;
    ll stk,mul;//mul为剩余的p^k-1的乘积
    ll fstk,fmul,orig;
}tree[mac<<2];

ll qpow(ll a,ll b)
{
    ll ans=1;
    a%=mod;
    while (b){
        if (b&1) ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans%mod;
}

void push_up(int rt)
{
    tree[rt].mul=tree[lc].mul*tree[rc].mul%mod;
    for (int i=0; i<cnt; i++)
        if ((tree[lc].stk&(1LL<<i)) && (tree[rc].stk&(1LL<<i)))
            tree[rt].mul=tree[rt].mul*prime[i]%mod;
    tree[rt].stk=tree[lc].stk|tree[rc].stk;
}

void push_down(int rt)
{
    for (int i=0; i<cnt; i++){
        if ((tree[lc].fstk&(1LL<<i)) && (tree[rt].fstk&(1LL<<i)))
            tree[lc].fmul=tree[lc].fmul*prime[i]%mod;
        if ((tree[rc].fstk&(1LL<<i)) && (tree[rt].fstk&(1LL<<i)))
            tree[rc].fmul=tree[rc].fmul*prime[i]%mod;

        if ((tree[lc].stk&(1LL<<i)) && (tree[rt].fstk&(1LL<<i)))
            tree[lc].mul=tree[lc].mul*prime[i]%mod;
        if ((tree[rc].stk&(1LL<<i)) && (tree[rt].fstk&(1LL<<i)))
            tree[rc].mul=tree[rc].mul*prime[i]%mod;
    }
    tree[lc].fstk|=tree[rt].fstk; tree[rc].fstk|=tree[rt].fstk;
    tree[lc].fmul=tree[lc].fmul*tree[rt].fmul%mod;
    tree[rc].fmul=tree[rc].fmul*tree[rt].fmul%mod;
    tree[lc].orig=tree[rt].orig*tree[lc].orig%mod;
    tree[rc].orig=tree[rt].orig*tree[rc].orig%mod;

    
    tree[lc].stk|=tree[rt].fstk; tree[rc].stk|=tree[rt].fstk;
    tree[lc].mul=(tree[lc].mul*tree[rt].fmul%mod)*qpow(tree[rt].orig,tree[lc].r-tree[lc].l)%mod;
    tree[rc].mul=(tree[rc].mul*tree[rt].fmul%mod)*qpow(tree[rt].orig,tree[rc].r-tree[rc].l)%mod;
    tree[rt].fstk=0;tree[rt].fmul=1;tree[rt].orig=1;
}

void build(int l,int r,int rt)
{
    tree[rt].stk=0;  tree[rt].mul=1;
    tree[rt].fstk=0; tree[rt].fmul=1;
    tree[rt].orig=1;
    tree[rt].l=l; tree[rt].r=r;
    if (l==r){
        int x;
        scanf ("%d",&x);
        for (int i=0; i<cnt; i++){
            if (x==1) break;
            if (x%prime[i]) continue;
            tree[rt].stk|=1LL<<i;
            x/=prime[i];
            while (!(x%prime[i])) tree[rt].mul=tree[rt].mul*prime[i]%mod,x/=prime[i];
        }
        return;
    }
    int mid=(l+r)>>1;
    build(lson);build(rson);
    push_up(rt);
}

void update(int l,int r,int rt,int L,int R,int val)
{
    if (l>=L && r<=R){
        ll tmp=0,s=1;
        int x=val;
        tree[rt].orig=tree[rt].orig*val%mod;
        for (int i=0; i<cnt; i++){
            if (x==1) break;
            if (x%prime[i]) continue;
            tmp|=1LL<<i;
            x/=prime[i];
            while (!(x%prime[i])) s=s*prime[i]%mod,x/=prime[i];
        }
        for (int i=0; i<cnt; i++){
            if ((tree[rt].fstk&(1LL<<i)) && (tmp&(1LL<<i)))
                tree[rt].fmul=tree[rt].fmul*prime[i]%mod;
            if ((tree[rt].stk&(1LL<<i)) && (tmp&(1LL<<i)))
                tree[rt].mul=tree[rt].mul*prime[i]%mod;
        }
        tree[rt].fstk|=tmp;
        tree[rt].fmul=s*tree[rt].fmul%mod;
        tree[rt].stk|=tmp;
        tree[rt].mul=(tree[rt].mul*s%mod)*qpow(val,r-l)%mod;
        return;
    }
    int mid=(l+r)>>1;
    if (tree[rt].fstk) push_down(rt);
    if (mid>=L) update(lson,L,R,val);
    if (mid<R) update(rson,L,R,val);
    push_up(rt);
}

ll query(int l,int r,int rt,int L,int R)
{
    ll ans=1;
    if (l>=L && r<=R){
        ll tmp=1;
        for (int i=0; i<cnt; i++){
            if (tree[rt].stk&(1LL<<i) && !(yes_stk&(1LL<<i))) {
                tmp=tmp*(prime[i]-1)%mod;
                yes_stk|=1LL<<i;
            }
            else if (tree[rt].stk&(1LL<<i) && (yes_stk&(1LL<<i)))
                tmp=tmp*prime[i]%mod;
            //if (L==4 && R==5)printf("yes_stk:%d %lld %lld \n",prime[i],tmp,yes_stk );
        }
        tmp=tmp*tree[rt].mul%mod;
        return tmp;
    }
    int mid=(l+r)>>1;
    if (tree[rt].fstk) push_down(rt);
    if (mid>=L) ans=ans*query(lson,L,R)%mod;
    if (mid<R) ans=ans*query(rson,L,R)%mod;
    return ans%mod;
}

int main(int argc, char const *argv[])
{
    int m=300;
    int s=sqrt(m);
    for (int i=2; i<=s; i++)
        if (!vis[i])
            for (int j=i*i; j<=m; j+=i)
                vis[j]=1;
    for (int i=2; i<=m; i++)
        if (!vis[i]) prime[cnt++]=i;
    int n,q;
    scanf ("%d%d",&n,&q);
    build(1,n,1);
    while (q--){
        int l,r,x;
        char s[15];
        scanf ("%s%d%d",s,&l,&r);
        yes_stk=0;
        if (s[0]==M){
            scanf ("%d",&x);
            update(1,n,1,l,r,x);
        }
        else {
            printf ("%lld\n",query(1,n,1,l,r));
        }
    }
    return 0;
}
View Code

然后,就开始想优化了,最后能够把枚举的去掉,然后。。。。。想了想,$(p_i-1)p_i^{k_i-1}$不就是$p_i^{k_i}/p_i*(p_i-1)$吗,那么我们直接维护乘积和状态就好了,由于数据只有300,我们完全可以预处理出每个数的素因子状态,那么枚举的62次就可以直接不用了,我们再最后得出结果的时候在判断就可以了。由于中间有除法,所以还要先预处理出每个数的逆元,不过这并不费事。。。

以下是AC代码:

#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;

#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define lc rt<<1
#define rc rt<<1|1
typedef long long ll;
const int mac=4e5+10;
const int mod=1e9+7;

int a[mac],vis[350],prime[100],cnt=0;
int inv[350];
ll yes_stk=0,every_stk[350];
struct node
{
    int l,r;
    ll stk,fstk;
    int mul,fmul;
}tree[mac<<2];

int qpow(int a,int b)
{
    int ans=1;
    a%=mod;
    while (b){
        if (b&1) ans=1LL*ans*a%mod;
        a=1LL*a*a%mod;
        b>>=1;
    }
    return ans%mod;
}

void push_up(int rt)
{
    tree[rt].mul=1LL*tree[lc].mul*tree[rc].mul%mod;
    tree[rt].stk=tree[lc].stk|tree[rc].stk;
}

void push_down(int rt)
{
    tree[lc].fstk|=tree[rt].fstk; tree[rc].fstk|=tree[rt].fstk;
    tree[lc].fmul=1LL*tree[lc].fmul*tree[rt].fmul%mod;
    tree[rc].fmul=1LL*tree[rc].fmul*tree[rt].fmul%mod;

    
    tree[lc].stk|=tree[rt].fstk; tree[rc].stk|=tree[rt].fstk;
    tree[lc].mul=1LL*tree[lc].mul*qpow(tree[rt].fmul,tree[lc].r-tree[lc].l+1)%mod;
    tree[rc].mul=1LL*tree[rc].mul*qpow(tree[rt].fmul,tree[rc].r-tree[rc].l+1)%mod;
    tree[rt].fstk=0;tree[rt].fmul=1;
}

void build(int l,int r,int rt)
{
    tree[rt].stk=0;  tree[rt].mul=1;
    tree[rt].fstk=0; tree[rt].fmul=1;
    tree[rt].l=l; tree[rt].r=r;
    if (l==r){
        int x;
        scanf ("%d",&x);
        tree[rt].stk|=every_stk[x];
        tree[rt].mul=x;
        return;
    }
    int mid=(l+r)>>1;
    build(lson);build(rson);
    push_up(rt);
}

void update(int l,int r,int rt,int L,int R,int val)
{
    if (l>=L && r<=R){
        tree[rt].fstk|=every_stk[val];
        tree[rt].fmul=1LL*val*tree[rt].fmul%mod;
        tree[rt].stk|=every_stk[val];
        tree[rt].mul=1LL*tree[rt].mul*qpow(val,r-l+1)%mod;
        return;
    }
    int mid=(l+r)>>1;
    if (tree[rt].fstk) push_down(rt);
    if (mid>=L) update(lson,L,R,val);
    if (mid<R) update(rson,L,R,val);
    push_up(rt);
}

int query(int l,int r,int rt,int L,int R)
{
    int ans=1;
    if (l>=L && r<=R){
        yes_stk|=tree[rt].stk;
        return tree[rt].mul;
    }
    int mid=(l+r)>>1;
    if (tree[rt].fstk) push_down(rt);
    if (mid>=L) ans=1LL*ans*query(lson,L,R)%mod;
    if (mid<R) ans=1LL*ans*query(rson,L,R)%mod;
    return ans%mod;
}

int main(int argc, char const *argv[])
{
    int m=300;
    int s=sqrt(m);
    for (int i=2; i<=s; i++)
        if (!vis[i])
            for (int j=i*i; j<=m; j+=i)
                vis[j]=1;
    for (int i=2; i<=m; i++)
        if (!vis[i]) prime[cnt++]=i;

    every_stk[1]=0;
    for (int i=2; i<=300; i++){
        ll tmp=0;
        int x=i;
        for (int j=0; j<cnt; j++)
            if (x%prime[j]) continue;
            else {
                tmp|=1LL<<j;
                while (!(x%prime[j])) x/=prime[j];
            }
        every_stk[i]=tmp;
    }

    for (int i=2; i<=300; i++) 
        inv[i]=qpow(i,mod-2);
    int n,q;
    scanf ("%d%d",&n,&q);
    build(1,n,1);
    while (q--){
        int l,r,x;
        char s[15];
        scanf ("%s%d%d",s,&l,&r);
        yes_stk=0;
        if (s[0]==M){
            scanf ("%d",&x);
            update(1,n,1,l,r,x);
        }
        else {
            int ans=query(1,n,1,l,r);
            for (int i=0; i<cnt; i++)
                if (yes_stk&(1LL<<i)){
                    ans=(1LL*ans*inv[prime[i]]%mod)*(prime[i]-1)%mod;
                }
                    
            printf ("%d\n",ans);
        }
    }
    return 0;
}

 

CodeForces 1114F--Please, another Queries on Array?(欧拉函数+线段树)

标签:一个   onclick   pac   开始   lap   using   src   ase   筛选素数   

原文地址:https://www.cnblogs.com/lonely-wind-/p/13288990.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!