NOIP 2018 保卫王国 (动态 DP)(LCT)

传送门
跟动态 DP 的模板差不多
考虑朴素的树形 d p dp dp
f u , 0 = ∑ v f v , 1 f_{u,0}=\sum_{v}f_{v,1} fu,0=vfv,1
f u , 1 = v a l [ u ] + ∑ v m i n ( f v , 0 , f v , 1 ) f_{u,1}=val[u]+\sum_{v}min(f_{v,0},f_{v,1}) fu,1=val[u]+vmin(fv,0,fv,1)
g u , 0 / 1 g_{u,0/1} gu,0/1 为所有虚儿子的和
f u , 0 = g u , 0 + f s o n , 0 , f u , 1 = g u , 1 + m i n ( f s o n , 0 , f s o n , 1 ) f_{u,0}=g_{u,0}+f_{son,0},f_{u,1}=g_{u,1}+min(f_{son,0},f_{son,1}) fu,0=gu,0+fson,0,fu,1=gu,1+min(fson,0,fson,1)
( f u , 0 , f u , 1 ) = ( f s o n , 0 , f s o n , 1 ) ∗ ( ∞ , g u , 1 g u , 0 , g u , 1 ) (f_{u,0},f_{u,1})=(f_{son,0},f_{son,1})*\binom{\infty,g_{u,1}}{g_{u,0},g_{u,1}} (fu,0,fu,1)=(fson,0,fson,1)(gu,0,gu,1,gu,1)
然后 l c t lct lct 维护虚子树的系数, a c c e s s access access 的时候切换


#include
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
typedef long long ll;
cs int N = 1e5 + 5;
int first[N], nxt[N << 1], to[N << 1], tot;
void add(int x, int y){
	nxt[++tot] = first[x], first[x] = tot, to[tot] = y;
}
int n, m, vl[N];
cs ll INF = 1e15;
void Mi(ll &a, ll b){ if(b < a) a = b; }
struct mat{
	ll a[2][2];
	mat(){ a[0][0] = a[0][1] = a[1][0] = a[1][1] = INF; }
	mat operator * (cs mat &A){
		mat B; for(int i = 0; i < 2; i++) for(int j = 0; j < 2; j++)
			for(int k = 0; k < 2; k++) Mi(B.a[i][j], a[i][k] + A.a[k][j]);
		return B;
	}
	ll mi(){ return min(a[1][0], a[1][1]); }
	void init(ll A, ll B){ a[0][1] = a[1][1] = B; a[1][0] = A; }
};

int fa[N], ch[N][2];
mat dp[N], trans[N];
void dfs(int u, int f){
	fa[u] = f;
	ll A = 0, B = vl[u];
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(t == f) continue;
		dfs(t, u); 
		A += dp[t].a[1][1];
		B += dp[t].mi();
	} 
	trans[u].init(A, B);
	dp[u].init(A, B);
}
#define ls ch[x][0]
#define rs ch[x][1]
bool isr(int x){ return ch[fa[x]][0]!=x && ch[fa[x]][1]!=x; }
int get(int x){ return ch[fa[x]][1] == x; }
void pushup(int x){ dp[x] = dp[rs] * trans[x] * dp[ls]; }
void rotate(int x){
	int y = fa[x], z = fa[y], k = get(x);
	if(!isr(y)) ch[z][get(y)] = x; fa[x] = z;
	ch[y][k] = ch[x][k^1]; fa[ch[x][k^1]] = y;
	ch[x][k^1] = y; fa[y] = x; pushup(y); pushup(x);
}
void splay(int x){
	while(!isr(x)){
		int y = fa[x], z = fa[y];
		if(!isr(y)) get(x) ^ get(y) ? rotate(x) : rotate(y); rotate(x);
	}
}
void access(int x){
	for(int y = 0; x; y = x, x = fa[x]){
		splay(x); 
		trans[x].a[0][1] += dp[rs].mi() - dp[y].mi();
		trans[x].a[1][1] += dp[rs].mi() - dp[y].mi();
		trans[x].a[1][0] += dp[rs].a[1][1] - dp[y].a[1][1];
		rs = y; pushup(x);
	}
}
void upt(int x, int opt, ll v){
	access(x); splay(x); 
	if(opt == 0){
		trans[x].a[0][1] += v;
		trans[x].a[1][1] += v;
	}
	else{
		trans[x].a[1][0] += v;
	} pushup(x);
}
int main(){
	n = read(), m = read(); 
	dp[0].a[0][0] = 0;
	dp[0].a[1][1] = 0;
	char opt[5]; scanf("%s", opt);
	for(int i = 1; i <= n; i++) vl[i] = read();
	for(int i = 1; i < n; i++){
		int x = read(), y = read();
		add(x, y); add(y, x);
	} dfs(1, 0);
	while(m--){
		int a = read(), x = read(), b = read(), y = read();
		upt(a, x, INF);
		upt(b, y, INF);
		splay(1);
		if(dp[1].mi() >= INF) puts("-1");
		else cout << dp[1].mi() << '\n';
		upt(a, x, -INF);
		upt(b, y, -INF);
	} return 0;
}

你可能感兴趣的:(LCT)