1. 程式人生 > >[UOJ#268]. 【清華集訓2016】資料互動[動態dp+可刪堆維護最長鏈]

[UOJ#268]. 【清華集訓2016】資料互動[動態dp+可刪堆維護最長鏈]

題意

給出 \(n\) 個點的樹,每個時刻可能出現一條路徑 \(A_i\) 或者之前出現的某條路徑 \(A_i\) 消失,每條路徑有一個權值,求出在每個時刻過後能夠找到的權值最大的路徑 \(B\) 的權值是多少,權值是所有和該路徑 \(B\) 有交的路徑 \(A\) 的權值和。

\(n\leq 10^5\)

分析

引理:兩條樹上路徑有交,則一定有一條路徑經過另一條路徑的 \(lca\).

  • 根據上面的性質我們考慮用樹形dp的方式求解。

  • 將一條路徑的權值在每個點 \(x\) 關係分成兩種:
    • \(a\) :路徑的 \(lca\)\(x\)
    • \(b\)
      :路徑的 \(lca\)\(x\) 的祖先;
  • 假設現在已經選定了一條路徑 \(B\),那麼該路徑的權值就是途徑所有點的 \(a\)\(lca\)\(b\) 之和 .

  • 考慮動態dp,因為樹剖之後答案一定可以寫成一段輕鏈+一段重鏈+一段輕鏈的形式。

  • 然後全域性再用一個可刪堆維護每條重鏈的答案即可。

  • 總時間複雜度為 \(O(nlog^2n)\)

注意可刪堆取次大值時要兩次檢查堆頂是否要被刪除

程式碼

#include<bits/stdc++.h>
using namespace std;
#define go(u) for(int i=head[u],v=e[i].to;i;i=e[i].lst,v=e[i].to)
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
typedef long long LL;
inline int gi(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
    return x*f;
}
template<typename T>inline bool Max(T &a,T b){return a<b?a=b,1:0;}
template<typename T>inline bool Min(T &a,T b){return b<a?a=b,1:0;}
const int N=1e5 + 7,Nd=N<<2;
int n,m,edc;
int head[N],x[N],y[N],w[N];
struct edge{
    int lst,to;
    edge(){}edge(int lst,int to):lst(lst),to(to){}
}e[N*2];
void Add(int a,int b){
    e[++edc]=edge(head[a],b),head[a]=edc;
    e[++edc]=edge(head[b],a),head[b]=edc;
}
// slpf
int son[N],zson[N],fa[N],dep[N],in[N],top[N],rev[N],down[N],tim;
void dfs1(int u){
    son[u]=1;
    go(u)if(v^fa[u]){
        fa[v]=u,dep[v]=dep[u]+1,dfs1(v);
        son[u]+=son[v];
        if(son[v]>son[zson[u]]) zson[u]=v;
    }
}
void dfs2(int u,int from){
    top[u]=from,in[u]=++tim,rev[tim]=u,down[u]=u;
    if(zson[u]) dfs2(zson[u],from),down[u]=down[zson[u]];
    go(u)if(v^fa[u]&&v^zson[u]) dfs2(v,v);
}
int Lca(int x,int y){
    for(;top[x]^top[y];y=fa[top[y]])
    if(dep[top[x]]>dep[top[y]]) swap(x,y);
    return dep[x]<dep[y]?x:y;
}
//sgt
#define Ls o<<1
#define Rs o<<1|1
struct data{
    LL l,r,s,mx;
    data(){}
    data operator +(const data &rhs)const{
        data res;
        res.l=max(l,s+rhs.l);
        res.r=max(rhs.r,rhs.s+r);
        res.s=s+rhs.s;
        res.mx=max(max(mx,rhs.mx),r+rhs.l);
        return res;
    }
}t[N<<2];
char s[10];
LL addv[Nd],g[Nd],se[Nd];
void st1(int o,LL v){
    t[o].r+=v,t[o].mx+=v;
    addv[o]+=v;
}
void pushup(int o){
    t[o]=t[Ls]+t[Rs];
}
void pushdown(int o){
    if(!addv[o]) return;
    st1(Ls,addv[o]);
    st1(Rs,addv[o]);
    addv[o]=0;
}
void ma(int p,int l,int r,int o,int opt,LL v){
    if(l==r){
        if(!opt){
            t[o].l+=v,t[o].r+=v,t[o].s+=v,t[o].mx+=v;
        }else if(opt==1){
            LL x=v-g[o];g[o]=v;
            t[o].l+=x,t[o].r+=x,t[o].mx+=x;
        }else{
            LL x=v-se[o];se[o]=v;
            t[o].mx+=x;
        }
        return;
    }
    pushdown(o);int mid=l+r>>1;
    if(p<=mid) ma(p,l,mid,Ls,opt,v);
    else ma(p,mid+1,r,Rs,opt,v);
    pushup(o);
}
void mb(int L,int R,int l,int r,int o,LL v){
    if(L>R) return;
    if(L<=l&&r<=R){
        st1(o,v);
        return;
    }
    pushdown(o);int mid=l+r>>1;
    if(L<=mid) mb(L,R,l,mid,Ls,v);
    if(R>mid)  mb(L,R,mid+1,r,Rs,v);
    pushup(o);
}
data query(int L,int R,int l,int r,int o){
    if(L<=l&&r<=R) return t[o];
    pushdown(o);int mid=l+r>>1;
    if(R<=mid) return query(L,R,l,mid,Ls);
    if(L>mid)  return query(L,R,mid+1,r,Rs);
    return query(L,R,l,mid,Ls)+query(L,R,mid+1,r,Rs);
}
struct Heap{
    priority_queue<LL>A,B;
    void push(LL x){A.push(x);}
    void pop(LL x){B.push(x);}
    LL top(){
        while(!B.empty()&&A.top()==B.top()) A.pop(),B.pop();
        return A.empty()?0:A.top();
    }
    LL se(){
        while(!B.empty()&&A.top()==B.top()) A.pop(),B.pop();
        if(A.empty()) return -1;
        LL x=A.top();A.pop();
        while(!B.empty()&&A.top()==B.top()) A.pop(),B.pop();
        if(B.empty()) {A.push(x);return -1;}
        LL y=A.top();A.push(x); 
        return y;
    }
}h[N],ans;
LL tans[N];
void upd(int u,int lca,LL v){
    int x=u;
    for(;u;u=fa[top[u]]){
        int gg=fa[top[u]];
        
        data res=query(in[top[u]],in[down[u]],1,n,1);
        h[gg].pop(res.l);
        if(tans[top[u]]) ans.pop(tans[top[u]]);
        
        if(u==x&&!lca) ma(in[u],1,n,1,0,v);
        else{
            if(lca) mb(max(in[top[u]],in[lca]+1),in[u],1,n,1,v);
            ma(in[u],1,n,1,1,h[u].top());
        }
        
        if(h[u].se()!=-1) ma(in[u],1,n,1,2,h[u].se());
        res=query(in[top[u]],in[down[u]],1,n,1);
        h[gg].push(res.l);
        ans.push(tans[top[u]]=res.mx);
    }
}
void pre(int u){
    go(u)if(v^fa[u]) pre(v);
    h[fa[top[u]]].push(0),ans.push(0);
}
int main(){
    n=gi(),m=gi();
    rep(i,1,n-1) Add(gi(),gi());
    dep[1]=1,dfs1(1),dfs2(1,1);
    pre(1);
    rep(i,1,m){
        scanf("%s",s);
        if(s[0]=='+'){
            x[i]=gi(),y[i]=gi(),w[i]=gi();
            int lca=Lca(x[i],y[i]);
            upd(lca,0,w[i]);
            upd(x[i],lca,w[i]);
            upd(y[i],lca,w[i]);
        }else{
            int t=gi(),lca=Lca(x[t],y[t]);
            upd(lca,0,-w[t]);
            upd(x[t],lca,-w[t]);
            upd(y[t],lca,-w[t]);
        }
        printf("%lld\n",ans.top());
    }
    return 0;
}