[bzoj4855][Jsoi2016]轻重路径【树链剖分】【线段树】

【题目链接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=4855
【题解】
  考虑一个轻重路径剖分,当有一个新的节点被加入后,最多改变 O(log) O ( l o g ) 个路径的剖分。
  那我们考虑时间倒流每次加入一个节点,那么改变的只可能是这个点到根的轻边,对于每个轻边,拿它与当前重边比较。比较时先比较size,再比较下一个节点插进来的时间。由于是时间倒流,所以时间晚的排名靠前。
  时间复杂度 O(Nlog2N) O ( N ∗ l o g 2 N )
【代码】

/* --------------
    user VanishD
    problem bzoj-4855
----------------*/
# include 
# define    ll      long long
# define    inf     0x3f3f3f3f
# define    N       200010
using namespace std;
int read(){
    int tmp=0, fh=1; char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
    while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
    return tmp*fh;
}
struct Edge{
    int data, next;
}e[N*2];
struct Tree{
    int pl, pr, l, r, size, ti, mx;  
}T[N*3];
int size[N], per[N], head[N], place, num[N], q[N], up[N], id,
    l[N], r[N], p[N], dad[N], tmp, ti[N], rt, k, n;
bool use[N], tag[N];
ll ans[N], sum, tot;
void build(int u, int v){
    e[++place].data=v; e[place].next=head[u]; head[u]=place;
}
void dfs1(int x, int fa){
    size[x]=1;
    for (int ed=head[x]; ed!=0; ed=e[ed].next)
        if (e[ed].data!=fa){
            dfs1(e[ed].data,x);
            if (size[per[x]]x]=e[ed].data;
            size[x]=size[x]+size[e[ed].data]; 
        }
}
void dfs2(int x, int fa, int an){
    p[x]=++id; dad[p[x]]=p[fa]; up[p[x]]=p[an]; 
    l[p[x]]=id;
    if (per[x]!=0) dfs2(per[x],x,an);
    for (int ed=head[x]; ed!=0; ed=e[ed].next)
        if (e[ed].data!=fa&&e[ed].data!=per[x])
            dfs2(e[ed].data,x,e[ed].data);
    r[p[x]]=id;
}
void findelse(int x, int fa){
    if (use[x]) return;
    for (int ed=head[x]; ed!=0; ed=e[ed].next)
        if (e[ed].data!=fa)
            findelse(e[ed].data,x);
    q[++tmp]=x;
}

void change(int p){
    T[p].mx=max(T[T[p].pl].mx,T[T[p].pr].mx);
    T[p].size=T[T[p].pl].size+T[T[p].pr].size;
    T[p].ti=max(T[T[p].pl].ti,T[T[p].pr].ti);
}
int create(int l, int r){
    int p=++place;
    T[p].l=l, T[p].r=r;
    if (T[p].l!=T[p].r){
        int mid=(T[p].l+T[p].r)/2;
        T[p].pl=create(l,mid);
        T[p].pr=create(mid+1,r);
        change(p);
    }
    else T[p].mx=l, T[p].size=0, T[p].ti=ti[l];
    return p;
}
void modifysize(int p, int x){
    if (T[p].l==T[p].r){
        T[p].size+=1; return;
    }
    int mid=(T[p].l+T[p].r)/2;
    if (mid>=x) modifysize(T[p].pl,x);
        else modifysize(T[p].pr,x);
    change(p); 
}
int querysize(int p, int l, int r){
    if (T[p].l==l&&T[p].r==r)
        return T[p].size;
    int mid=(T[p].l+T[p].r)/2;
    if (mid>=r) return querysize(T[p].pl,l,r);
        else if (midreturn querysize(T[p].pr,l,r);
            else return querysize(T[p].pl,l,mid)+querysize(T[p].pr,mid+1,r);
}
void modifyti(int p, int x){
    if (T[p].l==T[p].r){
        T[p].ti=0; return;
    }
    int mid=(T[p].l+T[p].r)/2;
    if (mid>=x) modifyti(T[p].pl,x);
        else modifyti(T[p].pr,x);
    change(p); 
}
int queryti(int p, int l, int r){
    if (T[p].l==l&&T[p].r==r)
        return T[p].ti;
    int mid=(T[p].l+T[p].r)/2;
    if (mid>=r) return queryti(T[p].pl,l,r);
        else if (midreturn queryti(T[p].pr,l,r);
            else return max(queryti(T[p].pl,l,mid),queryti(T[p].pr,mid+1,r));
}
int querylight(int p, int l, int r){
    if (T[p].l==l&&T[p].r==r) return T[p].mx;
    int mid=(T[p].l+T[p].r)/2;
    if (mid>=r) return querylight(T[p].pl,l,r);
        else if (l>mid) return querylight(T[p].pr,l,r);
            else {
                int tmp=querylight(T[p].pr,mid+1,r);
                if (tmp!=-1) return tmp;
                return querylight(T[p].pl,l,mid); 
            }
}
void addlight(int p, int x, int tag){
    if (T[p].l==T[p].r){
        if (tag==1) T[p].mx=x;
            else T[p].mx=-1;
        return;
    }
    int mid=(T[p].l+T[p].r)/2;
    if (mid>=x) addlight(T[p].pl,x,tag);
        else addlight(T[p].pr,x,tag);
    change(p);
}
void reget(int x, int ths){
    if (x==0) return;
    if (per[x]){
        int size1=querysize(rt,l[per[x]],r[per[x]]), size2=querysize(rt,l[ths],r[ths]),
            ti1=queryti(rt,l[per[x]],r[per[x]]), ti2=queryti(rt,l[ths],r[ths]);
        if (size1x],1); addlight(rt,ths,0);
            sum=sum+num[per[x]]-num[ths];
            per[x]=ths;
        }
    }
    else {
        addlight(rt,ths,0);
        sum=sum-num[ths];
        per[x]=ths;
    }
}
void extend(int x){
    modifysize(rt,x);
    modifyti(rt,x);
    while (x!=0){
        int flag=querylight(rt,up[x],x);
        while (flag!=-1){
            reget(dad[flag],flag);
            if (flag==up[x]) break;
            x=flag-1;
            flag=querylight(rt,up[x],x);
        }
        x=dad[up[x]];
    }
} 
int main(){
    n=read();
    for (int i=1; i<=n; i++){
        int u=read(), v=read(); tag[u]=true;
        if (u!=0) build(i,u), build(u,i);
        if (v!=0) build(i,v), build(v,i); 
    }
    place=0;
    dfs1(1,0); dfs2(1,0,1);
    memset(per,0,sizeof(per));
    for (int i=1; i<=n; i++){
        num[p[i]]=i;
        tot=tot+num[p[i]];
    }
    tmp=k=read();
    for (int i=1; i<=tmp; i++){
        q[i]=read();
        use[q[i]]=true;
    }
    findelse(1,0); 
    for (int i=1; i<=n; i++)
        q[i]=p[q[i]], ti[q[i]]=i;
    memset(use,0,sizeof(use));
    for (int i=1; i<=n; i++)
        if (tag[i]) use[p[i]]=true;
    for (int i=1; i<=n; i++) tag[i]=use[i];

    rt=create(1,n); sum=tot;
    for (int i=n; i>=1; i--){
        extend(q[i]);
        ans[i]=tot-sum;
    }
    for (int i=1; i<=k+1; i++)
        printf("%lld\n",ans[i]);
    return 0;
}

你可能感兴趣的:(【树链剖分】,【线段树】)