No Link, Cut Tree!【长链剖分】【dsu on tree】

//---------------------------------------------------------------------------

本篇博客是19年9月份写的,因为改成了比赛题目,所以设置隐藏,现在重新发。

//----------------------------------------------------------------------------

题目链接:https://vjudge.net/problem/Gym-101484F

原题目给的是一棵完全二叉树,所以暴力就可以过。

这里假设它是一颗一般树,那么可以用dsu on tree+线段树用O(N*logN*logN)的时间复杂度解决,或者用长链剖分+线段树+前缀最大值+后缀最大值做到O(N*logN)的时间复杂度。

dsu on tree 做法如下:

线段树维护当前每层的剩余点权和,然后启发式合并。

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define mp make_pair
#define pb push_back
#define ls (o<<1)
#define rs (o<<1|1)
#define mid (l+r>>1)
#define ll long long
using namespace std;
const int N = 1e5+1000;
int n,m,val[N],s[N];
//-------------
int T[4*N];
void build(int o,int l,int r) {
    T[o] = 0;
    if(l==r) return;
    build(ls,l,mid);
    build(rs,mid+1,r);
}
void add(int o,int l,int r,int pos,int x) {
    if(l==r) {
        T[o] += x;
        return;
    }
    if(mid>=pos) add(ls,l,mid,pos,x);
    else add(rs,mid+1,r,pos,x);
    T[o] = max(T[ls],T[rs]);
}
//------------
vector<int>nxt[N];
int siz[N],son[N],deep[N];
void split(int u,int f,int d) {
    son[u] = 0;
    siz[u] = 1;
    deep[u] = d;
    add(1,1,n,d,val[u]);
    for(auto v:nxt[u]) {
        if(v==f)continue;
        split(v,u,d+1);
        siz[u] += siz[v];
        if(siz[son[u]]<siz[v]) son[u] = v;
    }
}
//---------------------
int ans[N],Son;
void add(int u,int f,int data) {
    add(1,1,n,deep[u],data*val[u]);
    for(auto v:nxt[u]) {
        if(v==f||v==Son) continue;
        add(v,u,data);
    }
}
void dfs(int u,int f,int opt) {
    for(auto v:nxt[u]) {
        if(v==f||v==son[u]) continue;
        dfs(v,u,0);              //计算轻儿子的答案,不保留贡献
    }
    if(son[u]) dfs(son[u],u,1),Son = son[u];  //计算重儿子的答案,保留贡献
    add(u,f,-1),Son = 0;          //遍历所有轻儿子,将贡献合并
    ans[u] = T[1];             //记录答案
    if(!opt) add(u,f,1);  //如果u点是轻儿子,消除全部影响,清空所有信息
}
int main() {
    //freopen("a.txt","r",stdin);
    ios::sync_with_stdio(0);
    cin>>n>>m>>val[1];
    rep(i, 1, n-1) {
        int u,v;
        cin>>u>>v;
        cin>>val[u];
        nxt[v].pb(u);
    }
    build(1,1,n);
    split(1,0,1);
    dfs(1,0,0);
    rep(i, 1, m) {
        int x;
        cin>>x;
        cout<<ans[x]<<endl;
    }
    return 0;
}

长链剖分做法如下:

将整棵树的层分为3部分,第一部分是当前子树上面的层,第二部分是当前子树所在的层,第三部分是当前子树以下的层。

然后三部分取max,作为删除当前子树的答案。

pre维护层数的前缀最大值。

suf维护层数的后缀最大值。

dp维护子树内不同深度的点权和。

线段树维护整棵树中当前子树所在的各层点权剩余值。

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define mp make_pair
#define pb push_back
#define ls (o<<1)
#define rs (o<<1|1)
#define mid (l+r>>1)
#define ll long long
using namespace std;
const int N = 1e5+1000;
int n,m;
int T[4*N];
void build(int o,int l,int r) {
    T[o] = 0;
    if(l==r) return;
    build(ls,l,mid);
    build(rs,mid+1,r);
}
void add(int o,int l,int r,int pos,int x) {
    if(l==r) {
        T[o] += x;
        return;
    }
    if(mid>=pos) add(ls,l,mid,pos,x);
    else add(rs,mid+1,r,pos,x);
    T[o] = max(T[ls],T[rs]);
}
int query(int o,int l,int r,int h,int t){
    if(l>=h&&r<=t) return T[o];
    int ans=0;
    if(mid>=h) ans=max(ans,query(ls,l,mid,h,t));
    if(mid<t)  ans=max(ans,query(rs,mid+1,r,h,t));
    return ans;
}
//-------------------
vector<int>nxt[N];
int son[N],len[N],val[N],deep[N],sum[N],pre[N],suf[N];
void split(int u,int f,int d) {
    son[u] = 0;
    len[u] = 1;
    sum[d] += val[u];
   // maxx[d] = max(sum[d],maxx[d-1]);
    deep[u] = d;
    for(auto v:nxt[u]) {
        if(v==f)continue;
        split(v,u,d+1);
        if(len[son[u]]<len[v]) son[u] = v,len[u] = len[v]+1;
    }
}
//-----------------------
int pos[N],dp[N],ans[N],cnt;
void dfs(int u,int f) {
    pos[u] = ++cnt;
    add(1,1,n,pos[u],sum[deep[u]]-val[u]);
    dp[pos[u]] = val[u];
    if(son[u]) dfs(son[u],u);
    for(auto v:nxt[u]) {
        if(v==f||v==son[u]) continue;
        dfs(v,u);
        rep(j, 0, len[v]-1) {
            dp[pos[u]+j+1] += dp[pos[v]+j];
            add(1,1,n,pos[u]+j+1,-dp[pos[v]+j]);
        }
    }
    ans[u] = max(ans[u],query(1,1,n,pos[u],pos[u]+len[u]-1));
    ans[u] = max(ans[u],pre[deep[u]-1]);
    ans[u] = max(ans[u],suf[deep[u]+len[u]]);
}
int main() {
   // freopen("a.txt","r",stdin);
    ios::sync_with_stdio(0);
    cin>>n>>m>>val[1];
    rep(i, 1, n-1) {
        int u,v;
        cin>>u>>v;
        nxt[v].pb(u);
        cin>>val[u];
    }
    build(1,1,n);
    split(1,0,1);
    rep(i, 1, n) pre[i] = max(pre[i-1],sum[i]);
    per(i, n, 1) suf[i] = max(suf[i+1],sum[i]);
    dfs(1,0);
    rep(i, 1, m) {
        int x;
        cin>>x;
        cout<<ans[x]<<endl;
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章