【GDOI2016模拟4.23】轻重路径

Description

树链剖分都会吧?
维护一个二叉树的链剖,若size()相同选左子树。
每次删除一个叶子节点,若出现size()相等保持原有的剖法。
每次询问所有重边所指向的点的编号和。
n<=2*10^5

Solution

用链剖来维护链剖。(高级,用splay维护splay)
发现每一个点都必属于一条重链,于是就用总和-链顶和就是答案。
那么我们用线段树维护每一段区间的链顶和T,和每个点重儿子的size()-轻儿子的size()
C
每次操作都相当于把这个点到root上所有链顶的父亲的C+1,其余点的C-1.
具体操作可以先把所有点-1,再把所有链顶的父亲+2.
然后,对于所有C<0的点,把它的儿子的T值交换(必有一个为0,一个不为0)。
维护C的最小值就行了。
常数特别大,带3个log……
不过这道题的时限是3s
O(∩_∩)O~

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 200005
using namespace std;
typedef long long ll;
struct note{int c;ll t;}t[N*3];
int size[N],son[N],fa[N],l[N],r[N],d[N],w[N],top[N];
int n,m,root,x,tot,add[N*3],c[N*3];
bool bz[N];
ll sum;
void back(int v,int x) {t[v].c+=x;add[v]+=x;}
void down(int v) {
    if (add[v]) {
        back(v*2,add[v]);
        back(v*2+1,add[v]);
        add[v]=0;
    }
}
note merge(note y,note z) {
    note x;x.t=y.t+z.t;
    x.c=min(y.c,z.c);
    return x;
}
void dfs(int x) {
    if (!x) return;size[x]=1;
    dfs(l[x]);dfs(r[x]);size[x]+=size[l[x]]+size[r[x]];
    d[l[x]]=d[r[x]]=d[x]+1;
    if (size[r[x]]>size[l[x]]) son[x]=r[x];else son[x]=l[x];
}
void make(int x,int y) {
    if (!x) return;
    top[x]=y;w[x]=++tot;c[tot]=x;
    if (!son[x]) return;
    make(son[x],y);
    if (l[x]!=son[x]) make(l[x],l[x]);
    else make(r[x],r[x]);
}
void change(int v,int l,int r,int x,int y,int z) {
    if (!x||!y||x>y) return;
    if (l==x&&r==y) {back(v,z);return;}
    int m=(l+r)/2;down(v);
    if (y<=m) change(v*2,l,m,x,y,z);
    else if (x>m) change(v*2+1,m+1,r,x,y,z);
    else change(v*2,l,m,x,m,z),change(v*2+1,m+1,r,m+1,y,z);
    t[v]=merge(t[v*2],t[v*2+1]);
}
void seek(int v,int l,int r) {
    if (l==r) {change(1,1,n,w[fa[c[l]]],w[fa[c[l]]],2);return;}
    int m=(l+r)/2;down(v);
    if (t[v*2].t) seek(v*2,l,m);
    if (t[v*2+1].t) seek(v*2+1,m+1,r);
    t[v]=merge(t[v*2],t[v*2+1]);
}
void push(int v,int l,int r,int x,int y) {
    if (l==x&&r==y) {seek(v,l,r);return;}
    int m=(l+r)/2;down(v);
    if (y<=m) {
        if (t[v*2].t) push(v*2,l,m,x,y);
    } else if (x>m) {
        if (t[v*2+1].t) push(v*2+1,m+1,r,x,y);
    } else {
        if (t[v*2].t) push(v*2,l,m,x,m);
        if (t[v*2+1].t) push(v*2+1,m+1,r,m+1,y);
    }
    t[v]=merge(t[v*2],t[v*2+1]);
}
void del(int x) {
    int f=top[x];
    while (f!=root) {
        change(1,1,n,w[f],w[x],-1);push(1,1,n,w[f],w[x]);
        x=fa[f];f=top[x];
    }
    change(1,1,n,w[f],w[x],-1);push(1,1,n,w[f],w[x]);
}
void revise(int v,int l,int r,int x) {
    if (!x) return;
    if (l==r) {
        bz[c[l]]=t[v].t^=c[l];
        return;
    }
    int m=(l+r)/2;down(v);
    if (x<=m) revise(v*2,l,m,x);
    else revise(v*2+1,m+1,r,x);
    t[v]=merge(t[v*2],t[v*2+1]);
}
void per(int v,int a,int b) {
    if (a==b) {
        revise(1,1,n,w[l[c[a]]]);
        revise(1,1,n,w[r[c[a]]]);
        t[v].c*=-1;return;
    }
    int m=(a+b)/2;down(v);
    if (t[v*2].c<0) per(v*2,a,m);
    if (t[v*2+1].c<0) per(v*2+1,m+1,b);
    t[v]=merge(t[v*2],t[v*2+1]);
}
void find(int v,int l,int r,int x,int y) {
    if (l==x&&r==y) {per(v,l,r);return;}
    int m=(l+r)/2;down(v);
    if (y<=m) {
        if (t[v*2].c<0) find(v*2,l,m,x,y);
    } else if (x>m) {
        if (t[v*2+1].c<0) find(v*2+1,m+1,r,x,y);
    } else {
        if (t[v*2].c<0) find(v*2,l,m,x,m);
        if (t[v*2+1].c<0) find(v*2+1,m+1,r,m+1,y);
    }
    t[v]=merge(t[v*2],t[v*2+1]);
}
void rev(int x) {
    int f=top[x];
    while (f!=root) {
        find(1,1,n,w[f],w[x]);
        x=fa[f];f=top[x];
    }
    find(1,1,n,w[f],w[x]);
}
int main() {
    freopen("heavy.in","r",stdin);
    freopen("heavy.out","w",stdout);
    scanf("%d",&n);sum=(ll)n*(n+1)/2;
    fo(i,1,n) scanf("%d%d",&l[i],&r[i]),
    fa[l[i]]=fa[r[i]]=i;
    fo(i,1,n) if (!fa[i]) root=i;
    dfs(root);make(root,root);
    fo(i,1,n) {
        if (son[i]==l[i]) x=size[l[i]]-size[r[i]];
        else x=size[r[i]]-size[l[i]];
        change(1,1,n,w[i],w[i],x);
        if (top[i]==i) revise(1,1,n,w[i]);
    }
    printf("%lld\n",sum-t[1].t);
    for(scanf("%d",&m);m;m--) {
        scanf("%d",&x);
        del(x);rev(x);
        if (bz[x]) revise(1,1,n,w[x]);
        if (x==l[fa[x]]) l[fa[x]]=0;
        else r[fa[x]]=0;
        fa[x]=0;sum-=x;
        printf("%lld\n",sum-t[1].t);
    }
}

嘿嘿,发扬码农精神,1h+打完A掉。

你可能感兴趣的:(树链剖分,GDOI2016模拟,轻重路径,4-23)