洛谷P4074/UOJ58[WC2013]糖果公园(莫队)

莫队之集大成者——树上带修改莫队。

首先对树dfs,记下进入和离开一个结点的时间戳$in_u$,$out_u$,并得到相应的长为$2n$的dfs序(常称为括号序),对于一个询问$(u,v)$:

  1. 若$lca_{u,v}=u$,则$in_u$到$in_v$之间$(u,v)$路径上的点恰好出现一次,其他点要么不出现,要么出现2次(进入一次,退出一次),$lca_{u,v}=v$同理;
  2. 否则,不妨设$out_u<in_v$(否则交换$u$,$v$即可),则$[out_u,in_v]$区间内$u$到$lca_{u,v}$的点出现一次(只有退出),$v$到$lca_{u,v}$的点出现一次(只有进入),其他点为0次或2次。但是注意,$lca_{u,v}$不在序列中出现,要单独保存。

那么就可以莫队维护了,每次记下$vis_u$表示$u$是否被算在贡献中,如果重复则删去,就可以找出所有出现一次的点,即u,v路径上的点。

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=100050;
char rB[1<<21],*rS,*rT,wB[(1<<21)+50];
int wp=-1;
inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<21,stdin),rS==rT)?EOF:*rS++;}
inline void flush(){fwrite(wB,1,wp+1,stdout);wp=-1;}
inline int rd(){
    char c=gc();
    while(c<48||c>57)c=gc();
    int x=c&15;
    for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15);
    return x;
}
short buf[25];
inline void wt(ll x){
    if(wp>(1<<21))flush();
    short l=-1;
    while(x>9){
        buf[++l]=x%10;
        x/=10;
    }
    wB[++wp]=x|48;
    while(l>=0)wB[++wp]=buf[l--]|48;
    wB[++wp]='\n';
}
int G[N],to[N<<1],nxt[N<<1],sz=0,f[18][N],dep[N],din[N],dout[N],pos[N<<1],dfsc=0,a[N],w[N],val[N],ps[N],pre[N],cur[N],K,cnt[N];
ll s[N],ans=0ll;
bool vis[N];
struct query{
    int l,r,t,p,id;
}Q[N];
inline bool cmp(query a,query b){
    if((a.l-1)/K!=(b.l-1)/K)return (a.l-1)/K<(b.l-1)/K;
    if((a.r-1)/K!=(b.r-1)/K)return (a.r-1)/K<(b.r-1)/K;
    return a.t<b.t;
}
inline void adde(int u,int v){
    to[++sz]=v;nxt[sz]=G[u];G[u]=sz;
    to[++sz]=u;nxt[sz]=G[v];G[v]=sz;
}
void dfs(int u,int fa){
    int i,v;
    dep[u]=dep[f[0][u]=fa]+1;
    for(i=1;(1<<i)<dep[u];++i)f[i][u]=f[i-1][f[i-1][u]];
    pos[din[u]=++dfsc]=u;
    for(i=G[u];i;i=nxt[i])if((v=to[i])!=fa)dfs(v,u);
    pos[dout[u]=++dfsc]=u;
}
inline int lca(int u,int v){
    short i;
    if(dep[u]<dep[v])swap(u,v);
    for(i=16;i>=0;--i)if(dep[u]-(1<<i)>=dep[v])u=f[i][u];
    if(u==v)return u;
    for(i=16;i>=0;--i)if(f[i][u]!=f[i][v]){u=f[i][u];v=f[i][v];}
    return f[0][u];
}
inline void add(int c){
    ans+=(ll)val[c]*w[++cnt[c]];
}
inline void del(int c){
    ans-=(ll)val[c]*w[cnt[c]--];
}
inline void ins(int p){
    if(vis[p])del(a[p]);
    else add(a[p]);
    vis[p]^=1;
}
int main(){
    int n=rd(),m=rd(),q=rd(),mt=0,mq=0,i,u,v,opt,l,x,y,tim;
    for(i=1;i<=m;++i)val[i]=rd();
    for(i=1;i<=n;++i)w[i]=rd();
    for(i=1;i<n;++i){
        u=rd();v=rd();
        adde(u,v);
    }
    dfs(1,0);
    for(i=1;i<=n;++i)a[i]=rd();
    for(i=0;i<q;++i){
        opt=rd();u=rd();v=rd();
        if(!opt){ps[++mt]=u;pre[mt]=a[u];a[u]=cur[mt]=v;}
        else{
            if(dep[u]>dep[v])swap(u,v);
            l=lca(u,v);
            if(l==u){Q[mq].l=din[u];Q[mq].r=din[v];}
            else{
                if(dout[u]>din[v])swap(u,v);
                Q[mq].l=dout[u];Q[mq].r=din[v];Q[mq].p=l;
            }
            Q[mq].t=mt;Q[mq].id=mq;++mq;
        }
    }
    K=pow(n,2.0/3);
    sort(Q,Q+mq,cmp);
    for(i=mt,tim=Q[0].t;i>tim;--i)a[ps[i]]=pre[i];
    for(i=x=Q[0].l,y=Q[0].r;i<=y;++i)ins(pos[i]);
    if(Q[0].p)ins(Q[0].p);
    s[Q[0].id]=ans;
    if(Q[0].p)ins(Q[0].p);
    for(i=1;i<mq;++i){
        for(;x<Q[i].l;++x)ins(pos[x]);
        while(x>Q[i].l)ins(pos[--x]);
        for(;y>Q[i].r;--y)ins(pos[y]);
        while(y<Q[i].r)ins(pos[++y]);
        while(tim<Q[i].t){
            ++tim;
            if(vis[ps[tim]]){del(pre[tim]);add(cur[tim]);}
            a[ps[tim]]=cur[tim];
        }
        for(;tim>Q[i].t;--tim){
            if(vis[ps[tim]]){del(cur[tim]);add(pre[tim]);}
            a[ps[tim]]=pre[tim];
        }
        if(Q[i].p)ins(Q[i].p);
        s[Q[i].id]=ans;
        if(Q[i].p)ins(Q[i].p);
    }
    for(i=0;i<mq;++i)wt(s[i]);
    flush();
    return 0;
}
View Code

 

posted @ 2019-08-28 14:33  wangyuchen  阅读(138)  评论(0编辑  收藏  举报