【题解】【BZOJ】BZOJ4712 洪水

 

 

BZOJ4712 洪水

BZOJ4712 洪水

1 题外话

鸽了好久

2 sol

首先考虑没有修改的情况,题目变成一个简单的dp

设\(f_i\) 表示从\(i\) 出发走不到所有其叶子节点的最小代价

则\(f_i=min(V_i,\sum_{t}f_t)\) 其中\(V_i\) 为删去\(i\) 点的权值,\(t\) 是\(i\) 的儿子

对树进行重链剖分,设\(g_i\) 表示\(i\) 的所有轻儿子的\(f\) 的和

则有\(f_i=min(V_i,g_i+f_son)\) ,其中\(son\) 表示\(i\) 的重儿子

将矩阵乘法写成min和+的形式

就有\(\begin{bmatrix}0&f_h\end{bmatrix}\times\begin{bmatrix}0&V_i\\\infty&g[i]\end{bmatrix}=\begin{bmatrix}0&f_i\end{bmatrix}\)

直接用树剖ddp即可

3 code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long

const int N=200010;
const int inf=2e9;

inline void read(int &x) {
    x=0;
    int f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {
        if (ch=='-') {
            f=-1;
        }
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') {
        x=x*10+ch-'0';
        ch=getchar();
    }
    x*=f;
}

struct note {
    int t;
    int next;
};

int n,m;
int cnt;
int head[N];
note e[N<<1];

inline void add(int x,int y) {
    e[++cnt].t=y;
    e[cnt].next=head[x];
    head[x]=cnt;
}

struct mat {
    int a[2][2];
    mat(int ax=inf,int b=inf,int c=inf,int d=inf) {
        a[0][0]=ax;
        a[0][1]=b;
        a[1][0]=c;
        a[1][1]=d;
    }
    inline void init_min() {
        for(int i=0;i<2;i++) {
            a[i][i]=0;
        }
    }
};

inline mat operator * (const mat &x,const mat &y) {
    mat res;
    for(int i=0;i<2;i++) {
        for(int k=0;k<2;k++) {
            for(int j=0;j<2;j++) {
                res.a[i][j]=min(res.a[i][j],x.a[i][k]+y.a[k][j]);
            }
        }
    }
    return res;
}

int val[N];

namespace HLD {

int tot;
int dep[N],fa[N],siz[N],son[N];
int dfn[N],idfn[N],top[N],bot[N];

void dfs1(int p,int fat) {
    dep[p]=dep[fat]+1;
    fa[p]=fat;
    siz[p]=1;
    int max_siz=-1;
    for(int i=head[p];i+1;i=e[i].next) {
        int t=e[i].t;
        if (t==fat) {
            continue;
        }
        dfs1(t,p);
        siz[p]+=siz[t];
        if (siz[t]>max_siz) {
            max_siz=siz[t];
            son[p]=t;
        }
    }
}

void dfs2(int p,int top_p) {
    top[p]=top_p;
    dfn[p]=++tot;
    idfn[tot]=p;
    bot[p]=p;
    if (son[p]) {
        dfs2(son[p],top_p);
        bot[p]=bot[son[p]];
    }
    for(int i=head[p];i+1;i=e[i].next) {
        int t=e[i].t;
        if (t==son[p]||t==fa[p]) {
            continue;
        }
        dfs2(t,t);
    }
}

mat t[N<<2],tmp[N];

void build(int p,int l,int r) {
    if (l==r) {
        t[p]=tmp[idfn[l]];
        return;
    }
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    t[p]=t[p<<1|1]*t[p<<1];
}

void modify(int p,int l,int r,int x) {
    if (l==r) {
        t[p]=tmp[idfn[l]];
        return;
    }
    int mid=(l+r)>>1;
    if (x<=mid) {
        modify(p<<1,l,mid,x);
    } else {
        modify(p<<1|1, mid+1, r, x);
    }
    t[p]=t[p<<1|1]*t[p<<1];
}

mat query(int p,int l,int r,int L,int R) {
    if (l==L&&r==R) {
        return t[p];
    }
    int mid=(l+r)>>1;
    if (R<=mid) {
        return query(p<<1,l,mid,L,R);
    }
    if (L>mid) {
        return query(p<<1|1,mid+1,r,L,R);
    }
    return query(p<<1|1,mid+1,r,mid+1,R)*query(p<<1,l,mid,L,mid);
}

int f[N],g[N];

void pre_dp(int p) {
    f[p]=val[p];
    if (son[p]) {
        pre_dp(son[p]);
        f[p]=min(f[p],f[son[p]]);
    } else {
        g[p]=inf;
    }
    for(int i=head[p];i+1;i=e[i].next) {
        int t=e[i].t;
        if (t==fa[p]||t==son[p]) {
            continue;
        }
        pre_dp(t);
        g[p]+=f[t];
    }
    f[p]=min(f[p]+g[p],val[p]);
    tmp[p]=mat(0,val[p],inf,g[p]);
}

inline int get(int p) {
    return query(1, 1, n, dfn[p], dfn[bot[p]]).a[0][1];
}

inline void mdf(int p) {
    while(p) {
        tmp[p]=mat(0,val[p],inf,g[p]);
        modify(1,1,n,dfn[p]);
        p=top[p];
        if (p==1) {
            break;
        }
        g[fa[p]]-=f[p];
        f[p]=get(p);
        g[fa[p]]+=f[p];
        p=fa[p];
    }
}

}

signed main() {
    memset(head,-1,sizeof(head));
    read(n);
    for(int i=1;i<=n;i++) {
        read(val[i]);
    }
    for(int i=1;i<n;i++) {
        int x,y;
        read(x),read(y);
        add(x,y);
        add(y,x);
    }
    HLD::dfs1(1,0);
    HLD::dfs2(1,1);
    HLD::pre_dp(1);
    HLD::build(1,1,n);
    read(m);
    for(int i=1;i<=m;i++) {
        char ch=getchar();
        while(ch!='Q'&&ch!='C') {
            ch=getchar();
        }
        int x;
        read(x);
        if (ch=='Q') {
            printf("%lld\n",HLD::get(x));
        } else {
            int v;
            read(v);
            val[x]+=v;
            HLD::mdf(x);
        }
    }
    return 0;
}

4 注意

矩阵乘法没有交换律(调了半个小时的罪魁祸首)

代码中线段树的询问写法和平常写的效果相同但不用多写一个单位矩阵(我不会找这种乘法定义下的单位矩阵)

记得开long long

Author: tt66ea

Created: 2021-07-15 周四 15:23

Validate

上一篇:Codeforces 70E Information Reform 题解


下一篇:java-如何在JDO中进行批处理Google DataStore键查找查询