【清华集训2016】数据交互

【清华集训2016】数据交互

img
img
img

比较神的\(DDP\)

首先对于给出的一条链我们分两部分统计:\(lca\)以及其他部分。

我们设两个变量\(w_i,g_i\)。一条路径的权值就是路径上所有点的\(w\)之和\(+g_{lca}\)。所以我们将修改\(w_{lca}\)的权值,修改其他点的\(g\)值。删除的时候就加一条相同的,权值相反的路径。

具体细节就有多了。

首先我们给每个点\(v\)开一个可删除堆表示\(v\)的虚子树中到\(v\)的最长链。再开一个全局的可删除堆记录每条链贡献的答案。我们发现,一条路径可以看成是重链上的\(u,v\)两点(其中\(u\)\(lca\)),以及这两点分别向虚子树伸出去的一条最长链(可以为空)。它的权值为这条链的\(g\)之和再加上\(w_u\)。当\(u\)等于\(v\)的时候,我们需要找最长链和次长链。

我们发现这就是一个最大连续子段和。

对于线段树上的一个节点,我们记录:

\(mx\)表示答案。

\(lmx\)表示从最左边代表的节点(深度最小)开始的最长链。

\(rmx\)表示从最右边代表的节点(深度最大)开始的最长链。

img

还要记录\(sum\)表示区间内所有节点的\(w\)只和。

一条重链的链顶的最长链就是这条重链对应的\(lmx\)

修改的时候暴力将\(lca\)以上\(log\)条重链原来对其父亲贡献的最长链删除掉。更新过后将新的最长链贡献上去。

更多细节就看代码吧:

#include<bits/stdc++.h>
#define ll long long
#define N 100005
 
using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

int n,m;
struct road {int to,next;}s[N<<1];
int h[N],cnt;
void add(int i,int j) {s[++cnt]=(road) {j,h[i]};h[i]=cnt;}

int fa[N],dep[N];
int size[N],son[N];

void dfs(int v) {
    size[v]=1;
    for(int i=h[v];i;i=s[i].next) {
        int to=s[i].to;
        if(to==fa[v]) continue ;
        dep[to]=dep[v]+1;
        fa[to]=v;
        dfs(to);
        if(size[son[v]]<size[to]) son[v]=to;
        size[v]+=size[to];
    }
}

int dfn[N],bot[N],top[N],id;
int lst[N];
void dfs2(int v,int tp) {
    dfn[v]=++id;
    lst[id]=v;
    top[v]=tp;
    bot[v]=v;
    if(son[v]) {
        dfs2(son[v],tp);
        bot[v]=bot[son[v]];
    }
    for(int i=h[v];i;i=s[i].next) {
        int to=s[i].to;
        if(to==fa[v]||to==son[v]) continue ;
        dfs2(to,to);
    }
}

struct tree {
    int l,r;
    ll lmx,rmx;
    ll mx,tag;
    ll sum;
    tree() {l=r=lmx=rmx=mx=tag=sum=0;}
}tr[N<<2];

struct Heap {
    priority_queue<ll>add,del;
    void Pop() {
        while(del.size()&&add.top()==del.top()) {
            add.pop();
            del.pop();
        }
    }
    void Push(ll x) {
        Pop();
        add.push(x);
    }
    void Del(ll x) {
        del.push(x);
    }
    ll Top() {
        Pop();
        return add.top();
    }
    ll Sec() {
        ll x=Top();
        Del(x);
        ll y=Top();
        Push(x);
        return y;
    }
}Ans,F[N];

tree operator +(const tree &a,const tree &b) {
    tree tem;
    tem.l=a.l,tem.r=b.r;
    tem.mx=max(a.rmx+b.lmx,max(a.mx,b.mx));
    tem.lmx=max(a.lmx,a.sum+b.lmx);
    tem.rmx=max(b.rmx,a.rmx+b.sum);
    tem.sum=a.sum+b.sum;
    return tem;
}

// sum 单点
//tag 链 
void Update(int v,int p) {
    int u=lst[p];
    ll fir=F[u].Top(),sec=F[u].Sec();
    tr[v].lmx=tr[v].sum+fir;
    tr[v].rmx=tr[v].tag+tr[v].sum+fir;
    tr[v].mx=tr[v].tag+tr[v].sum+fir+sec;
}

void build(int v,int l,int r) {
    tr[v].l=l,tr[v].r=r;
    if(l==r) {
        Update(v,l);
        return ;
    }
    int mid=l+r>>1;
    build(v<<1,l,mid),build(v<<1|1,mid+1,r);
    tr[v]=tr[v<<1]+tr[v<<1|1];
}

void Set(int v,ll f) {
    tr[v].mx+=f;
    tr[v].tag+=f;
//  tr[v].lmx+=f;
    tr[v].rmx+=f;
}

void down(int v) {
    if(tr[v].tag) {
        Set(v<<1,tr[v].tag),Set(v<<1|1,tr[v].tag);
        tr[v].tag=0;
    }
}

void add_mx(int v,int l,int r,ll f) {//修改给出的整条链 
    if(tr[v].l>r||tr[v].r<l) return ;
    if(l<=tr[v].l&&tr[v].r<=r) {
        Set(v,f);
        return ;
    }
    down(v);
    add_mx(v<<1,l,r,f),add_mx(v<<1|1,l,r,f);
    tr[v]=tr[v<<1]+tr[v<<1|1];
}

void add_sum(int v,int p,int f) {//修改lca 
    if(tr[v].l>p||tr[v].r<p) return ;
    tr[v].sum+=f;
    if(tr[v].l==tr[v].r) {
        tr[v].lmx+=f;
        tr[v].rmx+=f;
        tr[v].mx+=f;
        return ;
    }
    down(v);
    add_sum(v<<1,p,f),add_sum(v<<1|1,p,f);
    tr[v]=tr[v<<1]+tr[v<<1|1];
}

void update(int v,int p) {
    if(tr[v].l>p||tr[v].r<p) return ;
    if(tr[v].l==tr[v].r) {
        Update(v,p);
        return ;
    }
    down(v);
    update(v<<1,p),update(v<<1|1,p);
    tr[v]=tr[v<<1]+tr[v<<1|1];
}

tree query(int v,int l,int r) {
    if(l<=tr[v].l&&tr[v].r<=r) return tr[v];
    down(v);
    int mid=tr[v].l+tr[v].r>>1;
    if(r<=mid) return query(v<<1,l,r);
    else if(l>mid) return query(v<<1|1,l,r);
    else return query(v<<1,l,r)+query(v<<1|1,l,r);
}

int lca(int a,int b) {
    while(top[a]!=top[b]) {
        if(dep[top[a]]<dep[top[b]]) swap(a,b);
        a=fa[top[a]];
    }
    return dep[a]<dep[b]?a:b;
}

void Modify_chain(int a,int b,int f) {
    while(top[a]!=top[b]) {
        if(dep[top[a]]<dep[top[b]]) swap(a,b);
        add_mx(1,dfn[top[a]],dfn[a],f);
        a=fa[top[a]];
    }
    if(dep[a]>dep[b]) swap(a,b);
    add_mx(1,dfn[a],dfn[b],f);
}

void Del(int a,int b) {
    static tree tem;
    while(top[a]!=top[b]) {
        if(dep[top[a]]<dep[top[b]]) swap(a,b);
        tem=query(1,dfn[top[a]],dfn[bot[a]]);
        Ans.Del(tem.mx);
        a=fa[top[a]];
    }
    tem=query(1,dfn[top[a]],dfn[bot[a]]);
    Ans.Del(tem.mx);    
}

void ADD(int a,int b) {
    static tree tem;
    while(top[a]!=top[b]) {
        if(dep[top[a]]<dep[top[b]]) swap(a,b);
        tem=query(1,dfn[top[a]],dfn[bot[a]]);
        Ans.Push(tem.mx);
        a=fa[top[a]];
    }
    tem=query(1,dfn[top[a]],dfn[bot[a]]);
    Ans.Push(tem.mx);
}

void Modify(int a,int b,int w) {
    int f=lca(a,b);
    tree tem;
    for(int i=top[f];i;i=top[fa[i]]) {
        tem=query(1,dfn[i],dfn[bot[i]]);
        if(fa[i]) F[fa[i]].Del(tem.lmx);
        if(i!=top[f]) {
            Ans.Del(tem.mx);
        }
    }
    
    Del(a,b);
    add_sum(1,dfn[f],w);
    Modify_chain(a,b,w);
    Modify_chain(f,f,-w);
    ADD(a,b);
    for(int i=top[f];i;i=top[fa[i]]) {
        tem=query(1,dfn[i],dfn[bot[i]]);
        if(fa[i]) {
            F[fa[i]].Push(tem.lmx);
            update(1,dfn[fa[i]]);
        }
        if(i!=top[f]) Ans.Push(tem.mx);
    }
}

struct edge {int x,y,w;}e[N];
void out(int v) {
    int x=top[v];
    for(int i=dfn[x];;i++) {
        cout<<lst[i]<<" ";
        if(i==dfn[v]) return ;
    }
}

int main() {
    n=Get(),m=Get();
    int a,b;
    for(int i=1;i<n;i++) {
        a=Get(),b=Get();
        add(a,b),add(b,a);
    }
    dfs(1);
    dfs2(1,1);
    for(int i=1;i<=n;i++) F[i].Push(0),F[i].Push(0);
    build(1,1,n);
    for(int i=1;i<=n;i++) if(i==top[i]) Ans.Push(0);
    char op;
    for(int i=1;i<=m;i++) {
        while(op=getchar(),op!='+'&&op!='-');
        if(op=='+') {
            e[i].x=Get(),e[i].y=Get(),e[i].w=Get();
            Modify(e[i].x,e[i].y,e[i].w);
        } else {
            int id=Get();
            Modify(e[id].x,e[id].y,-e[id].w);
        }
        cout<<Ans.Top()<<"\n";
    }
    return 0;
}

转载于:https://www.cnblogs.com/hchhch233/p/10566528.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值