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;
}

 

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