树形DP

树形DP新手向教程

1.P1352 没有上司的舞会

void dfs(int u){
    for(int i=head[u], v; i; i=next[i]) {
        dfs(v=to[i]); 
        dp[u][0]+=max(dp[v][1], dp[v][0]); 
        dp[u][1]+=dp[v][0];
    } dp[u][1]+=d[u];
}

2.P2014 选课

void dfs(int u){
    dp[u][0]=0;
    for(int i=head[u], v; i; i=next[i]){
        dfs(v=to[i]);
        for(int t=m; t >= 0; t--)
            for(int j=t; j >= 0; j--)
                dp[u][t]=max(dp[u][t], dp[u][t-j]+dp[v][j]);
    }
    if(u != 0) for(int i=m; i > 0; i--) 
        dp[u][i]=dp[u][i-1]+w[u];
}

3.P1273 有线电视网(要记录一下所连叶子节点的个数)

4.P1270 “访问”美术馆(这题输入好神奇)

int dp(int u, int left){
	if(left <= 0) return 0;
	if(f[u][left] != -1) return f[u][left];
	if(!ch[u][0]) return f[u][left]=min(val[u], left/5);
	int ls=ch[u][0], rs=ch[u][1]; 
	if(left > d[ls]) f[u][left]=max(f[u][left], dp(ls, left-d[ls]));
	if(left > d[rs]) f[u][left]=max(f[u][left], dp(rs, left-d[rs]));
	for(int i=d[ls]+1; left-i-d[rs] >= 0; i++) 
		f[u][left]=max(f[u][left], dp(ls, i-d[ls])+dp(rs, left-d[rs]-i));
	return f[u][left];
}

5.P3360 偷天换日(只要在上面那题的基础上加上背包预处理)

6.[HNOI/AHOI2018]道路(这个倒推公式不难得出,但是卡空间巨恶心)

void dfs(int u, int x, int y){
	int p=num[u]=(top ? S[top--] : ++tot); 
	if(!s[u]){
		for(int i=0; i <= x; i++) for(int j=0; j <= y; j++) 
			f[p][i][j]=1LL*c[u]*(a[u]+i)*(b[u]+j); 
		return ;
	}
	dfs(s[u], x+1, y); dfs(t[u], x, y+1);
	int ls=num[s[u]], rs=num[t[u]];
	for(int i=0; i <= x; i++) for(int j=0; j <= y; j++) 
		f[p][i][j]=min(f[ls][i+1][j]+f[rs][i][j], f[ls][i][j]+f[rs][i][j+1]);
	S[++top]=ls, S[++top]=rs;
}

7.[ZJOI2007]时态同步(两次(dfs), 一次计算最大值,一次计算答案)

void dfs1(int u){
	for(int i=0, v; i < e[u].size(); i++) if(d[v=e[u][i].first] == 0 && v != s){
		d[v]=d[u]+e[u][i].second; dfs1(v); mx[u]=max(mx[u], max(d[v], mx[v]));
	}
}
ll dfs2(int u, int fa){
	ll ans=0; 
	for(int i=0, v; i < e[u].size(); i++) if((v=e[u][i].first) != fa) 
		ans+=mx[u]-max(mx[v], d[v])+dfs2(v, u);
	return ans;
}

8.[APIO2010]巡逻(第一次(BFS)求树的直径, 第二次(DP)求树的直径)

//DP求树的直径:
void dfs(int u, int fa){
	for(int i=0, v; i < e[u].size(); i++) if((v=e[u][i].first) != fa){
	    dfs(v, u); 
            l1=max(l1, d[u]+d[v]+e[u][i].second); 
            d[u]=max(d[u], d[v]+e[u][i].second);
    }
}

9.hihoCoder#1763 : 道路摧毁
(dp[i][j])表示把点(i)只与(j)集合相连的最小代价

void dfs(int u, int fa){
	if(belong[u] == 1) dp[u][2]=inf; if(belong[u] == 2) dp[u][1]=inf;
	for(int i=head[u], v; i; i=nxt[i]) if((v=to[i]) != fa){
		dfs(v, u);
		if(belong[u] != 2) dp[u][1]+=min(dp[v][1], dp[v][2]+w[i]);
		if(belong[u] != 1) dp[u][2]+=min(dp[v][2], dp[v][1]+w[i]);
	}
}

10.[ZJOI2008]骑士(去掉环上的一条边,从两端点开始(DP), 最后再把两端点的合并)

void find(int u, int fa){
	vis[u]=1;
	for(int i=head[u], v; i; i=nxt[i]) 
        if((v=to[i]) != fa){
		if(vis[v]){e=i; x1=u, x2=v; continue;}
		find(v, u);
	}
}
void dfs(int u, int fa){
	dp[u][0]=0; dp[u][1]=val[u];
	for(int i=head[u], v; i; i=nxt[i]) 
        if(i != e && (i^1) != e && (v=to[i]) != fa){
	    dfs(v, u); 
            dp[u][0]+=max(dp[v][0], dp[v][1]); 
            dp[u][1]+=dp[v][0];
	}
}
// in main
for(int i=1; i <= n; i++) if(!vis[i]){
	find(i, 0); 
        dfs(x1, 0); ll delta=dp[x1][0]; 
        dfs(x2, 0); ans+=max(delta, dp[x2][0]);
}

11.[IOI2008]Island(判环磨死我了,展现了(toposort)(dfs)的优势(QAQ))

void dp(int color, int s){
		int m=0, i, u=s, len=0;
		do{
			a[++m]=f[u]; dep[u]=1;
			for(i=head[u]; i; i=nxt[i]) if(dep[to[i]] > 1) 
            	{u=to[i]; b[m+1]=b[m]+w[i]; break;}
		}while(i);
		if(m == 2){
			for(int i=head[u]; i; i=nxt[i]) if(to[i] == s) len=max(len, w[i]);
			d[color]=max(d[color], f[u]+f[s]+len); return ;
		}
		for(i=head[u]; i; i=nxt[i]) if(to[i] == s) {b[m+1]=b[m]+w[i]; break;}
		for(i=1; i < m; i++) a[m+i]=a[i], b[m+i]=b[m+1]+b[i];
		q[L=R=1]=1;
		for(i=2; i < m*2; i++){
			while(L <= R && i-q[L] >= m) L++;
			d[color]=max(d[color], a[i]+a[q[L]]+b[i]-b[q[L]]);
			while(L <= R && a[q[R]]-b[q[R]] <= a[i]-b[i]) R--;
			q[++R]=i;
		}
}

12.[HNOI2014]米特运输

虚树入门
1.[SDOI2011]消耗战(树链剖分分配(dfn)与求(Lca), 再在虚树上(DP),重点在虚树的构建上)

void ins(int x){
	if(top == 1) {sta[++top]=x; return ;}
	int lca=LCA(sta[top], x); 
	if(lca == sta[top]) return ;
	while(top > 1 && dfn[sta[top-1]] >= dfn[lca]) pb(sta[top-1], sta[top]), top--;
	if(lca != sta[top]) pb(lca, sta[top]), sta[top]=lca; sta[++top]=x;
}
ll DP(int u){
	if(!G[u].size()) return mn[u]; ll sum=0;
	for(int i=0; i < G[u].size(); i++) sum+=DP(G[u][i]);
	G[u].clear(); return min(mn[u], sum);
}
// in main
   while(m--){
	int k=read(); for(int i=1; i <= k; i++) pk[i]=read(); sort(pk+1, pk+1+k, cmp);
	sta[top=1]=1; for(int i=1; i <= k; i++) ins(pk[i]);
	while(top) pb(sta[top-1], sta[top]), top--;
	printf("%lld
", DP(1));
}

2.[HNOI2014]世界树

3.[SDOI2015]寻宝游戏

原文地址:https://www.cnblogs.com/zerolt/p/9282386.html