【BZOJ4712]洪水(チェーン分割ツリー+ DP)

問題の意味

ツリーの右側にポイントを与え、1ポイントを削除すると、対応する価格をとり、子供がツリーの葉の部分木のいずれかのルートを得ることができないように、最小のコストを見つけ、毎回サブツリーを掲載し、支援の一つのポイントに右正の値が増加します

考え

セット(F [I] \)は\ f及びIのすべての息子を表す[i]はiはサブツリー、H回答を表し、[i]は、iの値の重みを表し、wは状態遷移方程式を一覧表示することは困難ではありません。

F [I] =分(W [i]とH [i])と

私が葉である場合、いくつかの議論を避けるために、正の無限大の時間に割り当てられます

各アレイの値が低減しないように修正動作のために、Wとしてのみ、増加するであろう
fの値がWに等しい点を持っている場合、明らか場合に次にどんなにその時間を増加させる、その値はFではありません変化(それはwが変更されない限り)

F値(セットポイントiは、父親FAである)、我々は、4つのケースに分割されている祖先の影響の点をデルタを変更する変形例のプロセスのある時点で設定されています

  1. \(デルタ= 0 \) それは(実際には、ほとんど2)破る、祖先を変更しません

  2. \(F [FA] == W [FA] \) 即ち、それはH fの値に影響を及ぼさない変更、H [FA] + =デルタ、デルタ= 0、 次のステップがオフ壊れます

  3. \(W [FA]> H [FA]、[FA]> H [FA] +デルタ\ w)の後、すなわちプラスデルタ、F [FA]デルタが追加され

  4. \([FA] \のLeq H [FA] +デルタ\ W [FA]> F [FA]、w)はデルタを加えた後、[FA] Fは[FA、Wとなる、すなわち、

メンテナンス分(WH)、H(親ノードは、ケース2を満たす)、H値に加え、いくつかのデルタこれらのパスを満た​​す点の上位3を見つけ、動作を変更F親ノードの値を修正するためのチェーン分割ツリーその後、新しいデルタ再帰的な修正を得ます

各追加の再帰時間は、ポイントは、ケース2に説明する再帰は再帰ツリー操作が分割された鎖のO(n)のレベルになるように\((^ 2N)\ログ O) 、合計時間そう複雑さがある()O(nlog ^ 2nの\ \)

コード

#include<bits/stdc++.h>
#define N 200005 
#define Min(x,y) ((x)<(y)?(x):(y))
#define Max(x,y) ((x)>(y)?(x):(y))
using namespace std;
typedef long long ll;
const ll INF = 100000000000000;
int n,m;
int seg[N],rev[N],top[N],dep[N],fa[N],size[N],son[N],hfu;
ll f[N],h[N],w[N];//f[i]=Min(h[i],w[i])
ll minn[N<<2],sign[N<<2];//由于只会询问叶子节点的h值,所以用sign表示 

struct Edge
{
    int next,to;
}edge[N<<1];int head[N],cnt=1;
void add_edge(int from,int to)
{
    edge[++cnt].next=head[from];
    edge[cnt].to=to;
    head[from]=cnt;
}

template <class T>
void read(T &x)
{
    char c;int sign=1;
    while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}

void dfs1(int rt)
{
    h[rt]=0;
    size[rt]=1;
    dep[rt]=dep[fa[rt]]+1;
    for(int i=head[rt];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa[rt]) continue;
        fa[v]=rt;
        dfs1(v);
        
        h[rt]+=f[v];
        size[rt]+=size[v];
        if(size[son[rt]]<size[v]) son[rt]=v;
    }
    if(size[rt]==1) h[rt]=INF;//避免讨论,把叶子赋为INF 
    f[rt]=Min(w[rt],h[rt]);
}
void dfs2(int rt)
{
    if(son[rt])
    {
        seg[son[rt]]=++hfu;
        rev[hfu]=son[rt];
        top[son[rt]]=top[rt];
        dfs2(son[rt]);
    }
    for(int i=head[rt];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa[rt]||v==son[rt]) continue;
        seg[v]=++hfu;
        rev[hfu]=v;
        top[v]=v;
        dfs2(v);
    }
}

void pushup(int rt)
{
    minn[rt]=Min(minn[rt<<1],minn[rt<<1|1]);
}
void add_sign(int rt,ll val) 
{
    minn[rt]-=val;
    sign[rt]+=val;
}
void pushdown(int rt)
{
    if(!sign[rt]) return;
    add_sign(rt<<1,sign[rt]);
    add_sign(rt<<1|1,sign[rt]);
    sign[rt]=0;
}
void modify(int rt,int l,int r,int x,int y,ll val)//区间加h 
{
    if(x<=l&&r<=y) return add_sign(rt,val);
    int mid=(l+r)>>1;
    pushdown(rt);
    if(x<=mid) modify(rt<<1,l,mid,x,y,val);
    if(y>mid) modify(rt<<1|1,mid+1,r,x,y,val);
    pushup(rt);
}
void update(int rt,int l,int r,int x)//单点更新
{
    if(l==r)
    {
        minn[rt]=w[rev[l]]-sign[rt];
        return;
    }
    int mid=(l+r)>>1;
    pushdown(rt);
    if(x<=mid) update(rt<<1,l,mid,x);
    else update(rt<<1|1,mid+1,r,x);
    pushup(rt); 
}
ll query_h(int rt,int l,int r,int x)//查询h值 
{
    if(l==r) return sign[rt];
    int mid=(l+r)>>1;
    pushdown(rt);
    if(x<=mid) return query_h(rt<<1,l,mid,x);
    else return query_h(rt<<1|1,mid+1,r,x);
}
ll query_min(int rt,int l,int r,int x,int y,ll det)//找满足minn>det的最左边 
{
    if(x<=l&&r<=y) 
    {
        if(minn[rt]>det) return l;
        if(l==r) return 0; 
    }
    int mid=(l+r)>>1;
    pushdown(rt);
    if(x<=mid&&y<=mid) return query_min(rt<<1,l,mid,x,y,det);
    if(x>mid&&y>mid) return query_min(rt<<1|1,mid+1,r,x,y,det);
    int R=query_min(rt<<1|1,mid+1,r,x,y,det);
    if(!R||R>mid+1) return R;//如果右边已经不行了就不用查左边了 
    int L=query_min(rt<<1,l,mid,x,y,det);//如果直接左右一起查时间复杂度不对 
    return L ? L : R; 
}
void build(int rt,int l,int r)
{
    if(l==r)
    {
        minn[rt]=w[rev[l]]-h[rev[l]];
        sign[rt]=h[rev[l]];
        return;
    }
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    pushup(rt);
}
void modify_edge(int y,ll det)//修改y以上的满足delta < w[i]-h[i]的点 
{
    if(!y||!det) return;
    while(y)
    {
        int c=query_min(1,1,n,seg[top[y]],seg[y],det);//找到最上面的满足条件的 
        if(!c) break; c=rev[c];
        if(c!=top[y])  { modify(1,1,n,seg[c],seg[y],det);y=fa[c];break; }
        modify(1,1,n,seg[top[y]],seg[y],det);
        y=fa[top[y]];
    }
    if(!y) return;
    
    ll t=Min(w[y],query_h(1,1,n,seg[y])),delta;
    modify(1,1,n,seg[y],seg[y],det);
    delta=Min(w[y],query_h(1,1,n,seg[y]))-t;
    modify_edge(fa[y],delta);
}

int main()
{
    read(n);
    for(int i=1;i<=n;++i) read(w[i]);
    for(int i=1;i<n;++i)
    {
        int x,y;
        read(x);read(y);
        add_edge(x,y);
        add_edge(y,x);
    }
    seg[1]=rev[1]=top[1]=hfu=1;
    dfs1(1); dfs2(1);
    
    build(1,1,n);
    read(m);
    while(m--)
    {
        char op[2]; 
        int x; ll val;
        scanf("%s",op); read(x);
        if(op[0]=='Q') printf("%lld\n",Min(w[x],query_h(1,1,n,seg[x])));
        else
        {
            read(val);
            ll now=Min(w[x],query_h(1,1,n,seg[x])),delta;
            w[x]+=val;
            update(1,1,n,seg[x]);
            delta=Min(w[x],query_h(1,1,n,seg[x]))-now;
            modify_edge(fa[x],delta);
        }
    }
    return 0;
}

おすすめ

転載: www.cnblogs.com/Chtholly/p/11583844.html