【GDOI 2016】第四题 疯狂动物城

Description

【GDOI 2016】第四题 疯狂动物城_第1张图片

Solution

题目好长,一看就烦

操作1:1,x,y,z把x到y的点权值加z。
操作2:2,x,y把x到y的点一次放进一个数组a里面,统计答案 ans=ni=1a[i]nij=1
操作3:3,x所有点的状态回到第x次修改后的。

码农题啊!!!害人啊!!!

不过知道是码农题,在比赛的最后30分钟,竟然毫不犹豫地对其宣战。
失败告终…………………………………………….TAT

对要求的答案拆分

ans=i=1na[i](ni+1)(ni)2

ans=i=1na[i]i2a[i](2n+1)i+a[i](n+1)n

在线维护

明显的可持久化线段树,如果要回到历史版本,经典的以时间为一轴的可持久化线段树。
文绉绉的!其实平常统计颜色的时候,当一个节点颜色不同会动态多开一个点。
这道题,当一个点被改过就多开一个点。

答案拆开后

线段树只用统计4个部分: 2
合并有些复杂,慢慢的细推,有利于锻炼思维。实在不懂就看程序。
因为序号的遍历方式可能是顺序也可能是逆序,所以每个区间要存两个遍历方式的值。
我打了个struct套struct,这样方便操作。

树上的

树链剖分就可以了。

注意,一不小心就爆int了

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
typedef long long ll;
const ll maxn=200007,mo=20160501,ni=10080251;
int i,j,k,l,n,m;
int first[maxn*2],next[maxn*2],last[maxn*2],a[maxn],num,tot,u,v;
int deep[maxn],f[maxn][21],size[maxn],son[maxn],top[maxn],w[maxn],fw[maxn],tmp,ceng;
int root[maxn],now;
ll ans;
ll chu3(ll x,ll y) {
    if (x%3)return y/3%mo*x%mo;return x/3%mo*y%mo;
}
struct sx{
    ll len,sqr,sum,ji;
    void kong(){len=sqr=sum=ji=0;}
    friend sx operator +(sx x,sx y){
        sx z;
        z.len=x.len+y.len;
        z.sum=(x.sum+y.sum)%mo;
        z.ji=(x.ji+y.ji+x.len*y.sum%mo)%mo;
        z.sqr=(x.sqr+y.sqr+x.len*x.len%mo*y.sum%mo+2*y.ji%mo*x.len%mo)%mo;
        return z;
    }
    void add(ll z){
        sum=(sum+z*len%mo)%mo; 
        ji=(ji+len*(len+1)%mo*ni%mo*z%mo)%mo;
        sqr=(chu3(2*len+1,len*(len+1)/2)*z%mo+sqr)%mo;
    }
}ans1,ans2;
struct node{
    sx up,down;
    int l,r,biao,shu;
}t[maxn*25],op;
void add(int x,int y){
    last[++num]=y;next[num]=first[x];first[x]=num;
}
void dfs1(int x,int y){
    int i,k=0;
    deep[x]=deep[y]+1;f[x][0]=y;size[x]=1;
    rep(i,x){
        if(last[i]!=y){
            dfs1(last[i],x);
            size[x]+=size[last[i]];
            if(size[last[i]]>k)k=size[last[i]],son[x]=last[i];
        }
    }
}
void dfs2(int x,int y){
    int i;
    w[x]=++tot,fw[tot]=x,top[x]=y;
    if(!son[x])return;
    dfs2(son[x],y);
    rep(i,x){
        if(last[i]!=f[x][0]&&last[i]!=son[x])dfs2(last[i],last[i]);
    }
}
int lca(int x,int y){
    int i;if(deep[x]y])swap(x,y);
    fod(i,20,0)if(deep[f[x][i]]>deep[y])x=f[x][i];
    if(deep[x]>deep[y])x=f[x][0];
    fod(i,20,0)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    if(x!=y)return f[x][0];return x;
}
node merge(node x,node y){
    node z;
    z.down=x.down+y.down;
    z.up=y.up+x.up;
    return z;
}
void build(int &x,int l,int r){
    x=++tmp;
    if(l==r){
        t[x].up.len=t[x].down.len=1;
        t[x].up.sum=t[x].down.sum=t[x].up.ji=t[x].down.ji=t[x].up.sqr=t[x].down.sqr=a[fw[l]];
        return;
    }
    int mid=(l+r)/2;
    build(t[x].l,l,mid);
    build(t[x].r,mid+1,r);
    t[x].down=t[t[x].l].down+t[t[x].r].down;
    t[x].up=t[t[x].r].up+t[t[x].l].up;
}
void back(int &x,int y,ll z){
    if(!x)return;
    t[++tmp]=t[x];x=tmp;
    t[x].shu=y;t[x].biao+=z;
    t[x].up.add(z);t[x].down.add(z);
}
void down(int x){
    if(t[x].biao){
        back(t[x].l,t[x].shu,t[x].biao);
        back(t[x].r,t[x].shu,t[x].biao);
        t[x].biao=0;
    }
}
void change(int &x,int l,int r,int y,int z,ll o){
    if(!x)return;
    down(x);
    if(l==y&&r==z){back(x,ceng,o);return;}   
    int mid=(l+r)/2;
    if(t[x].shu!=ceng){
        t[++tmp]=t[x];x=tmp;
        t[x].shu=ceng;
    }
    if(z<=mid)change(t[x].l,l,mid,y,z,o);
    else if(y>mid)change(t[x].r,mid+1,r,y,z,o);
    else change(t[x].l,l,mid,y,mid,o),change(t[x].r,mid+1,r,mid+1,z,o);
    t[x].down=t[t[x].l].down+t[t[x].r].down;
    t[x].up=t[t[x].r].up+t[t[x].l].up;
}
void gai(int x,int y,ll z){
   int f1=top[x],f2=top[y];root[++ceng]=root[now];
   while(f1!=f2){
       if(deep[f1]x,y),swap(f1,f2);
       change(root[ceng],1,n,w[f1],w[x],z);
       x=f[f1][0],f1=top[x];
   }
   if(deep[x]y])swap(x,y);
   change(root[ceng],1,n,w[y],w[x],z);
   now=ceng;
}
int chang(int x,int y){
    int o=lca(x,y);
    return deep[x]+deep[y]-2*deep[o]+1;
}
node find(int x,int l,int r,int y,int z){
    if(!x)return op;
    down(x);
    if(l==y&&r==z)return t[x];
    int mid=(l+r)/2;
    if(z<=mid)return find(t[x].l,l,mid,y,z);
    else if(y>mid)return find(t[x].r,mid+1,r,y,z);
    else return merge(find(t[x].l,l,mid,y,mid),find(t[x].r,mid+1,r,mid+1,z));
}
void zhao(int x,int y){
    ans1.kong(),ans2.kong();
    int o=lca(x,y),ff=top[x];
    while(deep[ff]>deep[o]){
        ans1=ans1+find(root[now],1,n,w[ff],w[x]).up;
        x=f[ff][0],ff=top[x];
    }
    ff=top[y];
    while(deep[ff]>deep[o]){
        ans2=find(root[now],1,n,w[ff],w[y]).down+ans2;
        y=f[ff][0],ff=top[y];
    }
    if(deep[x]y])ans1=ans1+find(root[now],1,n,w[x],w[y]).down;
    else ans2=find(root[now],1,n,w[y],w[x]).up+ans2;
    ans1=ans1+ans2;
}
int main(){
    freopen("zootopia.in","r",stdin);
    freopen("zootopia.out","w",stdout);
//  freopen("fan.in","r",stdin);
//  freopen("fan.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,n-1){
        scanf("%d%d",&k,&l);
        add(k,l);
        add(l,k);
    }
    dfs1(1,0);
    dfs2(1,1);
    fo(j,1,20){
        fo(i,1,n){
            f[i][j]=f[f[i][j-1]][j-1];
        }
    }
    fo(i,1,n)scanf("%d",&a[i]);
    build(root[0],1,n);
    for(;m;m--){
        scanf("%d%d",&k,&u);u^=ans;
        if(k==1){
            scanf("%d%d",&v,&l);v^=ans;
            gai(u,v,l);
        }
        else if(k==2){
            scanf("%d",&v);v^=ans;
            ll q=chang(u,v);
            zhao(u,v);
           // ans=ni;
           // ans=ans*((-(2*q+1)*ans1.ji%mo+(q+1)*q%mo*ans1.sum%mo+ans1.sqr)%mo+mo)%mo;
            ans=(q*(q+1)%mo*ans1.sum%mo-(2*q+1)*ans1.ji%mo+ans1.sqr+mo)%mo;
            ans=(ans*ni)%mo;
            printf("%lld\n",ans);
        }
        else{
            now=u;
        }
    }
}

你可能感兴趣的:(树链剖分,线段树,GDOI,可持久化线段树,主席树)