BNUOJ 24253 (弱校联盟第7天E题)

这道题比赛的时候并没有AC,赛后看别人博客学会的,其实我一开始的思路还是比较像的,这道题看到的第一眼想到了网络赛的那个单点修改的,因为Mod不是质数,但是题目说了单点除的数在前面一定会出现一个乘这个数,所以连线搞就好了,然后这题并不能这样做。然后我就想着把Mod质因数分解,如果Mod = p1 * p2 * … Pn这种,所有质数的指数都是1的话,这个时候直接维护每一个质数的模的乘积,然后最后答案用一个CRT就搞定了,然后以为数据会给的这么水,结果发现样例都贵了。。。
下面说一下正解:
1:Mod 质因数分解。得到Mod的所有质因数fac[fac_cnt]
2:因为题目说了一定能够整除,所有我们线段树维护cnt[i],该区间乘积中Mod的第i个质因数的指数,然后我们发现 num[i]=fac[p[i]]q[i]k 那么剩下来的这个k,满足gcd(k,Mod) = 1。那么我们需要额外维护一个k。
3:对于M操作:M L R x 我们先把x写成上面那种形式,那么对于Mod的质因数,乘法就变成了指数的加法,对于k只需要乘起来就好了。
4:对于D操作:D L R x 和上面一样,但是除法变成了指数的减法,对于k变成逆元就搞定了

本题坑点:一开始的数可能是0,乘的数也可能是0;因为这个TLE了2发。

下面附上AC代码:

#include <bits/stdc++.h>
#define LL long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define lrt rt<<1
#define rrt rt<<1|1
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r

using namespace std;

const int maxn = 10010;

int n;
int fac[35],fac_cnt;
LL num[maxn];
LL Mod;

void exgcd(LL a,LL b,LL& d,LL& x,LL& y){
    if(!b)  {d = a; x = 1; y = 0;}
    else {exgcd(b,a%b,d,y,x); y -= x*(a/b);}
}

LL inv(LL a,LL p){
    LL d,x,y;
    exgcd(a,p,d,x,y);
    return d == 1 ? (x+p)%p : -1;
}

LL quickpow(LL a,LL x,LL p){
    LL ans = 1;
    while(x){
        if(x&1) ans = (ans*a)%p;
        a = (a*a)%p;
        x >>= 1;
    }
    return ans;
}

void init(){
    scanf("%d%d",&n,&Mod);
    LL m = Mod;
    fac_cnt = 0;
    for(int i = 2;i*i <= m;i ++)    if(m % i == 0){
        fac[fac_cnt++] = i;
        while(m % i == 0)   m /= i;
    }
    if(m != 1)  fac[fac_cnt++] = m;
    FOR(i,1,n+1)    scanf("%lld",&num[i]);
}

struct Tree{
    int l,r;
    LL cnt[30];
    LL lazy[30];
    LL x;
    LL lazyx;
}tree[maxn<<2];

void PushUp(int rt){
    FOR(i,0,fac_cnt)    tree[rt].cnt[i] = tree[lrt].cnt[i] + tree[rrt].cnt[i];
    tree[rt].x = (tree[lrt].x * tree[rrt].x)%Mod;
}

void UpDate(int rt,LL* a,LL y){
    FOR(i,0,fac_cnt)    tree[rt].lazy[i] += a[i];
    FOR(i,0,fac_cnt)    tree[rt].cnt[i] += a[i]*(tree[rt].r-tree[rt].l+1);
    tree[rt].lazyx = (tree[rt].lazyx*y)%Mod;
    LL tem = quickpow(y,(tree[rt].r-tree[rt].l+1),Mod);
    tree[rt].x = (tree[rt].x*tem)%Mod;
}

void PushDown(int rt){
    UpDate(lrt,tree[rt].lazy,tree[rt].lazyx);
    UpDate(rrt,tree[rt].lazy,tree[rt].lazyx);
    FOR(i,0,fac_cnt)    tree[rt].lazy[i] = 0;
    tree[rt].lazyx = 1;
}

void Build(int rt,int l,int r){
    tree[rt].l = l; tree[rt].r = r; tree[rt].lazyx = 1;
    FOR(i,0,fac_cnt)    tree[rt].lazy[i] = 0;
    if(l == r){
        FOR(i,0,fac_cnt){
            tree[rt].cnt[i] = 0;
            if(num[l] == 0) continue;
            while(num[l] % fac[i] == 0)    {tree[rt].cnt[i]++;num[l] /= fac[i];}
        }
        tree[rt].x = num[l];
        return;
    }
    int mid = (l+r)>>1;
    Build(lson);
    Build(rson);
    PushUp(rt);
}

void Modify(int rt,int l,int r,LL* a,LL y){
    if(tree[rt].l == l && tree[rt].r == r){
        UpDate(rt,a,y);
        return;
    }
    PushDown(rt);
    int mid = (tree[rt].l + tree[rt].r) >> 1;
    if(r <= mid)    Modify(lrt,l,r,a,y);
    else if(l > mid)    Modify(rrt,l,r,a,y);
    else{
        Modify(lson,a,y);
        Modify(rson,a,y);
    }
    PushUp(rt);
}

struct Ans{
    LL cnt[30];
    LL x;
};

Ans Query(int rt,int l,int r){
    if(tree[rt].l == l && tree[rt].r == r){
        Ans rhs;
        FOR(i,0,fac_cnt)    rhs.cnt[i] = tree[rt].cnt[i];
        rhs.x = tree[rt].x;
        return rhs;
    }
    PushDown(rt);
    int mid = (tree[rt].l + tree[rt].r)>>1;
    if(r <= mid)    return  Query(lrt,l,r);
    else if(l > mid)    return Query(rrt,l,r);
    else{
        Ans rhs1,rhs2;
        rhs1 = Query(lson);
        rhs2 = Query(rson);
        Ans rhs;
        FOR(i,0,fac_cnt)    rhs.cnt[i] = rhs1.cnt[i] + rhs2.cnt[i];
        rhs.x = (rhs1.x*rhs2.x)%Mod;
        return rhs;
    }
}

void work(){
    Build(1,1,n);
    int q;
    char op[2];
    scanf("%d",&q);
    while(q--){
        int l,r;
        LL w;
        scanf("%s%d%d",op,&l,&r);
        if(op[0] == 'Q'){
            Ans res = Query(1,l,r);
            LL ans = 1;
            ans = (ans*res.x)%Mod;
            if(!ans)    {printf("0\n");continue;}
            FOR(i,0,fac_cnt)    ans = (ans*quickpow(fac[i],res.cnt[i],Mod))%Mod;
            printf("%lld\n",ans);
        }
        else if(op[0] == 'M'){
            scanf("%lld",&w);
            LL a[30],y;
            memset(a,0,sizeof(a));
            if(!w)  {FOR(i,0,fac_cnt)    a[i] = 0; y = 0;}
            else{
                FOR(i,0,fac_cnt)    while(w % fac[i] == 0){
                    a[i] ++;
                    w /= fac[i];
                }
                y = w;
            }
            Modify(1,l,r,a,y);
        }
        else{
            scanf("%lld",&w);
            LL a[30],y;
            memset(a,0,sizeof(a));
            FOR(i,0,fac_cnt)    while(w % fac[i] == 0){
                a[i] ++;
                w /= fac[i];
            }
            if(w == 1)  y = w;
            else    y = inv(w,Mod);
            FOR(i,0,fac_cnt)    a[i] = -a[i];
            Modify(1,l,r,a,y);
        }
    }
}

int main(){
    //freopen("test.in","r",stdin);
    int T,tCase = 0;
    scanf("%d",&T);
    while(T--){
        printf("Case #%d:\n",++tCase);
        init();
        work();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值