BZOJ4105: [ThuSC2015]平方运算(并查集+线段树)

传送门

题意:
支持区间平方 (modp) ,区间求和( p10000 且为质数)。

题解:
每个数在平方意义下有循环节,且所有循环节的 gcd70 ,那么每个点维护 70 个数就好了。

在进入循环前会有一段不循环的区域(呈 ρ 形),对于之前的直接暴力修改+并查集维护即可。

#include<bits/stdc++.h>
using namespace std;
inline int rd(){
    char ch=getchar();int i=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
    return i*f;
}
inline void W(int x){
    static int buf[50];
    if(!x){putchar('0');return;}
    if(x<0){putchar('-');x=-x;}
    while(x){buf[++buf[0]]=x%10;x/=10;}
    while(buf[0])putchar(buf[buf[0]--]+'0');
}
const int LIM=80;
const int N=1e5+50;
int n,m,p,v[N][LIM],nst[N],st[N],vis[N],pos[N],bg[N],R[N],vt;
inline int gcd(int x,int y){return y?(gcd(y,x%y)):x;}
inline int getr(int x){return (R[x]==x)?x:(R[x]=getr(R[x]));}
struct node{
    int cir[LIM],nowst,sze,tag;
    node *lc,*rc;
    node(){
        nowst=1;sze=1;tag=0;
    }
    inline int getnxt(int v){
        return (v==sze)?1:v+1;
    }
    inline void push(int v){
        tag+=v;
        (nowst+=v)%=sze;
        if(!nowst)nowst=sze;
    }
}Pool[N*4],*rt,*pool=Pool;
inline void build(node *&now,int l,int r){
    now=++pool;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(now->lc,l,mid);
    build(now->rc,mid+1,r);
}
inline void pushdown(node *now){
    if(!now->tag)return;
    now->lc->push(now->tag);now->rc->push(now->tag);
    now->tag=0;
}
inline void upt(node *now){
    now->sze=now->lc->sze*now->rc->sze/gcd(now->lc->sze,now->rc->sze);
    now->tag=0;now->nowst=1;int p1=now->lc->nowst,p2=now->rc->nowst;
    for(int i=1;i<=now->sze;i++){
        now->cir[i]=now->lc->cir[p1]+now->rc->cir[p2];
        p1=now->lc->getnxt(p1);
        p2=now->rc->getnxt(p2);
    }
}
inline void insert(node *now,int l,int r,int pos){
    if(l==r){
        now->sze=st[pos]-bg[pos]+1;
        for(int i=1;i<=now->sze;++i)now->cir[i]=v[pos][bg[pos]+i-1];
        now->tag=0;now->nowst=1;
        return;
    }
    pushdown(now);int mid=(l+r)>>1;
    if(pos<=mid)insert(now->lc,l,mid,pos);
    else insert(now->rc,mid+1,r,pos);
    upt(now);
}
inline void modify(node *now,int l,int r,int L,int R){
    if(L<=l&&r<=R){
        now->push(1);
        return;
    }
    pushdown(now);int mid=(l+r)>>1;
    if(R<=mid)modify(now->lc,l,mid,L,R);
    else if(L>mid)modify(now->rc,mid+1,r,L,R);
    else modify(now->lc,l,mid,L,R),modify(now->rc,mid+1,r,L,R);
    upt(now);
}
inline int query(node *now,int l,int r,int L,int R){
    if(L<=l&&r<=R)return now->cir[now->nowst];
    pushdown(now);int mid=(l+r)>>1,rs=0;
    if(R<=mid)rs=query(now->lc,l,mid,L,R);
    else if(L>mid)rs=query(now->rc,mid+1,r,L,R);
    else return rs=query(now->lc,l,mid,L,R)+query(now->rc,mid+1,r,L,R);
    return upt(now),rs;
}
inline int query(int l,int r){
    int rs=query(rt,1,n,l,r);
    for(int i=getr(l);i<=r;i=getr(i+1)){
        rs+=v[i][nst[i]];
    }
    return rs;
}
inline void modify(int l,int r){
    modify(rt,1,n,l,r);
    for(int i=getr(l);i<=r;i=getr(i+1)){
        ++nst[i];
        if(nst[i]==bg[i]){
            insert(rt,1,n,i);
            R[i]=i+1;
        }
    }
}
int main(){
    n=rd(),m=rd(),p=rd();
    build(rt,1,n);
    for(int i=1;i<=n+1;i++)R[i]=i;
    for(int i=1;i<=n;i++){
        int x=rd()%p;++vt;
        while(vis[x]!=vt){
            v[i][++st[i]]=x;
            pos[x]=st[i];
            vis[x]=vt;
            x=1ll*x*x%p;
        }bg[i]=pos[x];
        nst[i]=1;
        if(nst[i]==bg[i])insert(rt,1,n,i),R[i]=i+1;
    }
    for(int i=1;i<=m;i++){
        int op=rd(),l=rd(),r=rd();
        if(op==0){
            modify(l,r);
        }else W(query(l,r)),putchar('\n');
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值