1. 程式人生 > 其它 >【題解】【BZOJ】BZOJ4712 洪水

【題解】【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