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;
}
}
虚树入门
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\n", DP(1));
}