[GDOI2016][树链剖分+主席树]疯狂动物城

题面:

Description

Nick 是只在动物城以坑蒙拐骗为生的狐狸,儿时受到偏见的伤害,放弃了自己的理想。他被兔子 Judy 设下圈套,被迫与她合作查案,而卷入意想不到的阴谋,历尽艰险后成为搭档。他们识破了绵羊副市长 Bellwether 的计划,发现是 Bellwether 陷害食肉动物,用毒药让食肉动物发狂。Bellwether 被抓到了监狱里面, Nick 和 Judy 过上了一段平静的日子。

然而,故事并没有这样结束,之前在车管所帮他们查车牌号的憨厚的树懒 Flash,才是陷害食肉动物事件的幕后主使。Flash 批量制作了大量让食肉动物发狂的药剂,投放到了食肉动物群中。现在,大量的食肉动物被感染,动物城陷入了一片混乱。警察局的牛局长 Bogo 找到了 Nick,希望他能帮忙。幸运的是,动物城联邦安全局非常有先见之明,他们在每个州都秘密放置了一台机器,机器能生产能量石,这些能量石能让食肉动物恢复正常。现在 Nick 和 Judy 需要去启动这些机器。

动物城是一个有 N 个州的联邦,该联邦是一个树的形状,即 N 个州共有 N 1 条双向道路连接它们,且 N 个州是相互连通的。 N 个州的编号依次为 1 , 2 , 3 , , N 。每个州都有且仅有一台机器。一台机器启动后的下一个时刻,就会开始生产能量石, 每个单位时间生产一个。能量石从被生产的时刻开始即生效,每一个单位时间能救一定数量的食肉动物。每个州的解毒机器制造出的能量石的品种可能是不同,第 i 个州的机器生产的能量石每个单位时间能救 a i 只食肉动物。

Nick 和 Judy 剩下的时间不多了,他们决定分工合作。 Nick 从 X 州出发,目的地为 Y 州,路径为 X Y 的最短路径。 Nick 从 X 州出发的时刻为 0 ,每隔一个单位时间移动一个州。每到一个州, Nick 就会启动这个州的机器。 Nick 想知道他从 X 州出发到达 Y 州的这段时间里,一共有多少食肉动物被拯救。 Nick 在纠结他的路线选择,因此,他会给你若干的询问,希望比他更聪明的你能帮助他。

在他给你询问的过程中,动物城的局势也在发生着一些变化。动物城联邦安全局可以执行一个修改操作 " X , Y , d e l t a " ,会对 X 州到 Y 州的最短路径上的州(包括 X , Y 州)的机器进行升级,这样,这些机器生产出来的能量石,每个单位时间能救的食肉动物的数量会增加 d e l t a 。树懒 Flash 当然也不会坐以待毙,他有一台监控仪,会监控每个州的机器的情况,每当有机器被升级,监控仪就会保存下当前所有州的机器的属性 a i 。 Flash可以用一种神秘的武器执行一个读取操作 " X " ,把当前各个州的机器恢复到第 X 次保存的状态($X=0表示未进行过升级时的初始状态)。注意, 只有修改操作执行的时后会进行保存。

现在,依次给出 M 个操作,若该操作为一个询问,请你输出 Nick 在当前局面下,他从 X 州出发到达 Y 州的这段时间里,一共有多少食肉动物被拯救,由于这个答案可能很大,你只需要输出答案模 20160501 后的值。请注意, M 个操作都是被加密过的。

Input

第一行 2 个整数 N , M 表示节点个数和操作次数。

接下来 N 1 每行 2 个整数 U i , V i 表示了这棵树中 U i V i 2 个州间有边相连。

接下来一行 N 个整数, 表示这 N 个州的机器制造的能量的初始值。

接下来 M 行每行先有一个数字表示了操作的类型:

类型 1 ,代表一个修改操作,接下来有 3 个整数 X 1 , Y 1 , d e l t a , X 1 , Y 1 是加密后的数字。正确的 X = X 1 x o r l a s t a n s , Y = Y 1 x o r l a s t a n s l a s t a n s 为上次输出的的答案,如果之前没有输出过那么当成 0

类型 2,代表一个询问操作,接下来有 2 个整数 X 1 , Y 1 , 和修改操作一样,正确的 X = X 1 x o r l a s t a n s , Y = Y 1 x o r l a s t a n s l a s t a n s 为上次输出的的答案,如果之前没有输出过那么当成 0

类型 3,代表一次读取操作,接下来 1 个整数 X 1 ,正确的 X = X 1 x o r l a s t a n s L a s t a n s 为上次输出的的答案,如果之前没有输出过那么当成 0

Output

对于每个操作 2 ,输出一行,每行一个数,为所询问的答案模 20160501 后的值。

zootopia.in zootopia.out
5 6
1 2
2 3
3 4
4 5
1 2 3 4 5
1 1 5 2
3 0
1 1 3 2
1 3 4 2
3 2
2 1 5
73
5 4
1 2
1 3
2 4
3 5
1 1 1 2 2
1 1 4 2
2 1 4
3 12
2 13 8
12
4

HINT

所有的数据 1 a i , d e l t a 100000 。保证数据是合法的,不会读取没保存的局面,即 X 已经给出的修改操作次数。

N 100000 , M 100000 ;

一开始的初值为 1,2,3,4,5。

第 1 个操作为修改操作,修改后变为 3,4,5,6,7。

第 2 个操作为读取操作,读取第 0 次修改后的局面, a1…a5 变为 1,2,3,4,5。

第 3 个操作为修改操作,修改后变为 3,4,5,4,5。

第 4 个操作为修改操作,修改后变为 3,4,7,6,5。

第 5 个操作为读取操作,读取第 2 次修改后的局面, a1…a5 变为 3,4,5,4,5。

第 6 个操作为询问操作,询问从 1 出发走到 5 的时间内有多少食肉动物被拯救。

Nick 在时刻 4 走到州 5。

此时一共被拯救的动物数量为 ( 3 ) + ( 3 × 2 + 4 ) + ( 3 × 3 + 4 × 2 + 5 ) + ( 3 × 4 + 4 × 3 + 5 × 2 + 4 ) = 73

分析:

注意:生产的是能量石,而能量石不会消失,一直会有效果,所以每时刻拯救的动物的数量是递增的,而不是每时刻拯救固定数量!也就是说,询问的不是前缀和,而是双重前缀和!

首先,看到这道题我们就会想到树链剖分。那么我们直接树链剖分。

当统计答案时,我们令 L C A = l c a ( X , Y ) ,分成 [ X , L C A ] [ L C A , Y ] 两段分别处理。

  • 当点 P [ X , L C A ] 时,由等差数列求和公式,这个点贡献的答案为:

    a i × ( d e p i + d e p Y 2 d e p L C A ) ( d e p i + d e p Y 2 d e p L C A + 1 ) 2 = a i × ( d e p Y + d e p Y 2 4 d e p L C A d e p Y + 4 d e p L C A 2 2 d e p L C A ) + d e p i ( 1 + 2 d e p Y 4 d e p L C A ) + d e p i 2 2 = a i × [ d e p Y ( d e p Y + 1 ) 2 d e p L C A ( 2 d e p Y 2 d e p L C A + 1 ) ] + d e p i ( 1 + 2 d e p Y 4 d e p L C A ) + d e p i 2 2

  • 当点 P [ L C A , Y ] 时,由等差数列求和公式,这个点贡献的答案为:

    a i × ( d e p Y d e p i ) ( d e p Y d e p i + 1 ) 2 = a i × ( d e p Y + d e p Y 2 ) + d e p i ( 1 2 d e p Y ) + d e p i 2 2 = a i × d e p Y ( d e p Y + 1 ) + d e p i ( 1 2 d e p Y ) + d e p i 2 2

于是,我们可以维护权值总和权值与深度之积的总和权值与深度的平方之积的总和来统计答案。为了进行更新,我们还要维护深度的总和深度的平方的总和。当询问时,我们把 a i 的系数、 a i d e p i 的系数传进去即可。

这道题还需要访问历史状态,于是我们用可持久化线段树(主席树)进行维护。为了节省空间,采用标记永久化策略。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N=100001;
const ll MOD=20160501;
int n,m,tot,dfx,lst,head[N],v[N<<1],nxt[N<<1],num[N],dep[N],f[N],sum[N],son[N],id[N],dfn[N],pre[N];
void addedge(int x,int y){v[++tot]=y;nxt[tot]=head[x];head[x]=tot;}
void dfs1(int u=1){
    sum[u]=1;
    for(int i=head[u];~i;i=nxt[i]){
        if(v[i]!=f[u]){
            f[v[i]]=u;
            dep[v[i]]=dep[u]+1;
            dfs1(v[i]);
            sum[u]+=sum[v[i]];
            if(!son[u]||sum[v[i]]>sum[son[u]])son[u]=v[i];
        }
    }
}
void dfs2(int u=1,int top=1){
    id[u]=top;
    dfn[u]=++dfx;
    pre[dfx]=u;
    if(!son[u])return;
    dfs2(son[u],top);
    for(int i=head[u];~i;i=nxt[i])if(v[i]!=son[u]&&v[i]!=f[u])dfs2(v[i],v[i]);
}
struct PresidentTree{
    int nowrt,idx,rtx,rt[N];
    struct node{
        int ls,rs;
        ll sum,dep,dep2,sudep,sudep2,add;
    }t[N*50];
    void pushup(int o){
        t[o].sum=(t[t[o].ls].sum+t[t[o].rs].sum)%MOD;
        t[o].dep=(t[t[o].ls].dep+t[t[o].rs].dep)%MOD;
        t[o].dep2=(t[t[o].ls].dep2+t[t[o].rs].dep2)%MOD;
        t[o].sudep=(t[t[o].ls].sudep+t[t[o].rs].sudep)%MOD;
        t[o].sudep2=(t[t[o].ls].sudep2+t[t[o].rs].sudep2)%MOD;
    }
    void build(int &o,int l,int r){
        o=++idx;
        if(l==r){
            t[o].sum=num[pre[l]];
            t[o].dep=dep[pre[l]];
            t[o].dep2=dep[pre[l]]*dep[pre[l]]%MOD;
            t[o].sudep=t[o].sum*t[o].dep%MOD;
            t[o].sudep2=t[o].sum*t[o].dep2%MOD;
            return;
        }
        int mid=(l+r)>>1;
        build(t[o].ls,l,mid);
        build(t[o].rs,mid+1,r);
        pushup(o);
    }
    pair<ll,ll> update(int rt,int l,int r,int &o,int ql,int qr,ll c){
        if(ql>qr)swap(ql,qr);
        if(!o){
            o=++idx;
            t[o]=t[rt];
            t[o].ls=t[o].rs=0;
        }
        if(ql<=l&&r<=qr){
            t[o].add=(t[o].add+c)%MOD;
            t[o].ls=t[rt].ls;
            t[o].rs=t[rt].rs;
            return make_pair(t[o].dep,t[o].dep2);
        }
        int mid=(l+r)>>1;
        pair<ll,ll>p,tmp;
        if(qr<=mid){
            if(!t[o].rs)t[o].rs=t[rt].rs;
            if(t[o].ls==t[rt].ls)t[o].ls=0;
            p=update(t[rt].ls,l,mid,t[o].ls,ql,qr,c);
        }else if(ql>mid){
            if(!t[o].ls)t[o].ls=t[rt].ls;
            if(t[o].rs==t[rt].rs)t[o].rs=0;
            p=update(t[rt].rs,mid+1,r,t[o].rs,ql,qr,c);
        }else{
            if(t[o].ls==t[rt].ls)t[o].ls=0;
            p=update(t[rt].ls,l,mid,t[o].ls,ql,qr,c);
            if(t[o].rs==t[rt].rs)t[o].rs=0;
            tmp=update(t[rt].rs,mid+1,r,t[o].rs,ql,qr,c);
            p.first+=tmp.first;
            p.second+=tmp.second;
        }
        t[o].sum=(t[o].sum+c*(min(qr,r)-max(ql,l)+1))%MOD;
        t[o].sudep=(t[o].sudep+c*p.first)%MOD;
        t[o].sudep2=(t[o].sudep2+c*p.second)%MOD;
        return p;
    }
    ll query(int o,int l,int r,int ql,int qr,ll tag,ll s1,ll s2){
        tag=(tag+t[o].add)%MOD;
        if(ql>qr)swap(ql,qr);
        if(ql<=l&&r<=qr)return ((t[o].sum+tag*(r-l+1))%MOD*s1+(t[o].sudep+tag*t[o].dep)%MOD*s2+(t[o].sudep2+tag*t[o].dep2)%MOD)%MOD;
        ll re=0;
        int mid=(l+r)>>1;
        if(ql<=mid)re+=query(t[o].ls,l,mid,ql,qr,tag,s1,s2);
        if(qr>mid)re+=query(t[o].rs,mid+1,r,ql,qr,tag,s1,s2);
        return re%MOD;
    }
}pt;
void update(int x,int y,ll del){
    ++pt.rtx;
    while(id[x]!=id[y]){
        if(dep[id[x]]<dep[id[y]])swap(x,y);
        pt.update(pt.rt[pt.nowrt],1,n,pt.rt[pt.rtx],dfn[id[x]],dfn[x],del);
        x=f[id[x]];
    }
    pt.update(pt.rt[pt.nowrt],1,n,pt.rt[pt.rtx],dfn[x],dfn[y],del);
    pt.nowrt=pt.rtx;
}
int LCA(int x,int y){
    while(id[x]!=id[y]){
        if(dep[id[x]]<dep[id[y]])swap(x,y);
        x=f[id[x]];
    }
    return dep[x]<=dep[y]?x:y;
}
ll query(int x,int y){
    ll ans=0;
    int b=y,lca=LCA(x,y);
    while(id[x]!=id[y]){
        if(dep[id[x]]>dep[id[y]]){
            ans=(ans+pt.query(pt.rt[pt.nowrt],1,n,dfn[id[x]],dfn[x],0,((ll)dep[b]*(dep[b]+1)-2*dep[lca]*(2*dep[b]-2*dep[lca]+1)+MOD)%MOD,(1+2*dep[b]-4*dep[lca]+MOD)%MOD))%MOD;
            x=f[id[x]];
        }else{
            ans=(ans+pt.query(pt.rt[pt.nowrt],1,n,dfn[id[y]],dfn[y],0,((ll)dep[b]*(dep[b]+1))%MOD,(-1-2*dep[b]+MOD)%MOD))%MOD;
            y=f[id[y]];
        }
    }
    if(dep[x]>dep[y])ans=(ans+pt.query(pt.rt[pt.nowrt],1,n,dfn[y],dfn[x],0,((ll)dep[b]*(dep[b]+1)-2*dep[lca]*(2*dep[b]-2*dep[lca]+1)+MOD)%MOD,(1+2*dep[b]-4*dep[lca]+MOD)%MOD))%MOD;
    else ans=(ans+pt.query(pt.rt[pt.nowrt],1,n,dfn[x],dfn[y],0,((ll)dep[b]*(dep[b]+1))%MOD,(MOD-1-2*dep[b])%MOD))%MOD;
    return ans&1?(ans+MOD)>>1:ans>>1;
}
int main(){
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x,y);
        addedge(y,x);
    }
    for(int i=1;i<=n;i++)scanf("%d",&num[i]);
    dfs1();
    dfs2();
    pt.build(pt.rt[0],1,n);
    for(int i=1;i<=m;i++){
        int opt,x,y;
        ll del;
        scanf("%d",&opt);
        if(opt==1){
            scanf("%d%d%lld",&x,&y,&del);
            update(x^lst,y^lst,del);
        }else if(opt==2){
            scanf("%d%d",&x,&y);
            printf("%d\n",lst=query(x^lst,y^lst));
        }else if(opt==3){
            scanf("%d",&x);
            pt.nowrt=x^lst;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/EZ_TJY/article/details/81112151