一句话总结重点:第一次dfs搜索全部点,得出全部点状态的值,第二次dfs对于各类状态进行计算,从而得出所须要的答案。c++
总结:spa
第一次扫描时,任选一个点为根,在“有根树”上执行一次树形dp,在回溯时,自底向上的状态转移。设计
第二次扫描时,从第一次选的根出发,对整根树执行一个dfs,在每次递归前进行自顶向下的转移,计算出换根后的解。code
sizes[x]=1; ... dfs(y,x); sizes[x]+=sizes[y];
其余状态须要本身设置,通常来讲,树形dp的题目都是跟树的节点数目有关的。递归
进入到第二遍dfs中,咱们就须要运用 容斥原理 去求出答案:ci
例如P3047 [USACO12FEB]Nearby Cows G 中,咱们去计算距离不超过k的点的点权之和。由于在第一遍dfs中,咱们运用了:get
for(int j=1;j<=k;j++) f[x][j]+=f[y][j-1];
若是是这样的话,咱们对于距离为 \(x\) 的遍,都多算了 \((k-x)\) 次,所以在第二次dfs中有如下代码:it
for(int j=k;j>=2;j--) f[y][j]-=f[y][j-2]; for(int j=1;j<=k;j++) f[y][j]+=f[x][j-1];
void dfs(int x,int fa){ sizes[x]=1; for(int i=head[x];i;i=nxt[i]){ int y=ver[i]; if(y==fa) continue; dfs(y,x); sizes[x]+=size[y]; .... .... } } void dfs2(int x,int fa)[ for(int i=head[x];i;i=nxt[i]){ int y=ver[i]; if(y==fa) continue; ... ... ... ans[x]=... dfs(y,x); } ] int main() { cin>>n; for(int i=1;i<=n;i++){ cin>>x>>y; add(x,y);add(y,x); } dfs(1,0);dfs2(1,0); cout<<ans<<endl; return 0; }
解题思路:class
\(dp[i][1]\) 表示选择该节点所得到的最大值,\(dp[i][0]\) 表示不选择该节点得到的最大值。
所以咱们就能够轻松写出来代码:
#include<bits/stdc++.h> using namespace std; vector<int> son[10010]; int f[10010][2],v[10010],h[10010],n; void dp(int x){ f[x][0]=0;//0表示其自己不参加 f[x][1]=h[x]; //自己参加 for(int i=0;i<son[x].size();i++){ int y=son[x][i];//儿子的地址 dp(y); f[x][0]+=max(f[y][0],f[y][1]);//儿子节点参加或不参加的最大值 f[x][1]+=f[y][0];//每一个儿子节点不参加的最大值 } } int main() { cin>>n; for(int i=1;i<=n;i++) cin>>h[i]; for(int i=1;i<n;i++){ int x,y; cin>>x>>y; v[x]=1;//有爹 son[y].push_back(x); } int root; for(int i=1;i<=n;i++) if(!v[i]){//没爹,即根节点 root=i; break; } dp(root); cout<<max(f[root][1],f[root][0])<<endl; return 0; }
咱们知道,一个节点不能做为重心,有且仅有一个子树大小大于 \(\lfloor\dfrac{n}{2}\rfloor\),咱们必定是从这个子树里面选一个子树接在当前的根上。
那么就找一个这样的子树就能够啦
考虑 \(dp[u]\) 的最佳转移点,若是 \(dp[u]\) 最佳转移点就是\(v\) ,那么咱们就须要获得一个点第二大的可以去除的子树大小
因此必须考虑维护两个 \(dp\) 值,\(dp[u][0]\) 表示第一大的值,\(dp[u][1]\) 表示第二大的值。
这样咱们就能够解出这道题了。
#include<bits/stdc++.h> using namespace std; const int N=8e5+5; int n; int nxt[N],ver[N],head[N],tot; int dp[N][2],pos[N]; int sizes[N],maxsizes[N]; int ans[N],[N]; void add(int x,int y){ ver[++tot]=y; nxt[tot]=head[x]; head[x]=tot; } void dfs(int x,int fa){ sizes[x]=1; for(int i=head[x];i;i=nxt[i]){ int y=ver[i]; if(y==fa) continue; dfs(y,x); int v; sizes[x]+=sizes[y]; if(sizes[y]>sizes[maxsizes[x]]) maxsizes[x]=y;// 最多子节点的树 if(sizes[y]<=n/2) v=sizes[y];//子节点不足 else v=dp[y][0]; if(dp[x][0]<v){ dp[x][1]=dp[x][0]; dp[x][0]=v;pos[x]=y; } else if(dp[x][1]<v) dp[x][1]=v; } } void dfs2(int x,int fa){ ans[x]=1; if(sizes[maxsizes[x]]>n/2) ans[x]=(sizes[maxsizes[x]]-dp[maxsizes[x]][0]<=n/2); else if(n-sizes[x]>n/2) ans[x]=(n-sizes[x]-f[x]<=n/2); for(int i=head[x];i;i=nxt[i]){ int y=ver[i]; if(y==fa) continue; int v; if(n-sizes[x]>n/2) v=f[x]; else v=n-sizes[x]; f[y]=max(f[y],v); if(pos[x]==y) f[y]=max(f[y],dp[x][1]); else f[y]=max(f[y],dp[x][0]); dfs2(y,x); } } int main() { cin>>n; for(int i=1;i<n;i++){ int x,y;scanf("%d%d",&x,&y); add(x,y);add(y,x); } dfs(1,0);dfs2(1,0); for(int i=1;i<=n;i++) printf("%d ",ans[i]); //system("pause"); return 0; }