[CF1646D] Weight the Tree
[CF1646D] Weight the Tree
题意
给定一个无根树,点从 \(1\) 到 \(n\) 编号。你需要给每个点分配一个正整数权值 \(w_i\)。定义一个节点为好节点,当且仅当其权值等于所有相邻节点的权值之和。
请最大化好节点的个数,并且在好节点个数最大的前提下最小化所有节点的权值和。
分析
我们先考虑一种特殊情况,当 \(n=2\) 时我们有唯一可以使一个点和它的父亲都为好的节点,这样我们先特判这种情况。
而后我们考虑设两个状态来转移本题。我们设 \(f_{i,10}\) 表示该点是好的节点或不好的节点子树内好的节点数(包含该点)。显然我们有一个节点和它的父亲不能同时为好的节点。而后我们显然有转移
\(f_{u,0}=sum min({f_{son[u],0},f_{son[u],1}})\)
\(f_{u,1}=sum f_{sum[u],0}\)。
而后我们考虑设 \(g_{i,10}\) 表示该点是好的节点或者不好的节点子树内点权和(包含该点)。显然
\(g_{u,1}=sum g_{son[u],0}\)。
当 \(f_{u,0}=f_{u,1}\) 时有 \(g_{u,0}=sum min(g_{son[u],0},g_{son[u],1})\)。
当 \(f_{u,0}f_{u,1}\) 时有 \(g_{u,0}=sum f_{son[u],0}\)。
当 \(f_{u,0}f_{u,1}\) 时有 \(g_{u,0}=sum f_{son[u],1}\)。
而后我们考虑一种方案,即若该点为好的节点该点权值为 \(deg[i]\) 否则为 \(1\)。显然最优。而后我们考虑怎么标记。我们考虑再进行一次 \(mark\) 操作,如果该点的父亲被标记那么显然它肯定不能被标记,而后如果该点被该点不被标记是 \(f\) 值大或者 \(f\) 值相同 \(g\) 值更小,那么不被标记,反之被标记。而后我们下传这个标记给他的儿子,这样使得它的儿子必须不能被标记,这样 dfs 下去显然可以得出一个方案。
代码
#includebitsstdc++.h
using namespace std;
const int N=2e5+7;
int n;
vectorint G[N2];
int deg[N];
int f[N2][2],g[N2][2];
void dfs(int u,int father){
f[u][1]=1,g[u][1]=deg[u],g[u][0]=1;
for(int kG[u]) if(k!=father){
dfs(k,u);
f[u][1]+=f[k][0];
g[u][1]+=g[k][0];
f[u][0]+=max(f[k][0],f[k][1]);
if(f[k][0]==f[k][1]) g[u][0]+=min(g[k][0],g[k][1]);
else g[u][0]+=(f[k][0]f[k][1]g[k][0]g[k][1]);
}
}
bool mrk[N];
void mark(int u,int father,bool fl){
if(fl) mrk[u]=0;
else{
if(f[u][0]f[u][1]f[u][0]==f[u][1]&&g[u][0]g[u][1])
mrk[u]=0;
else mrk[u]=1;
}
for(int kG[u]){
if(k!=father)
mark(k,u,mrk[u]);
}
}
int main(){
scanf(%d,&n);
for(int i=1;i=n-1;i++){
int u,v;scanf(%d%d,&u,&v);G[u].push_back(v),G[v].push_back(u);
++deg[u],++deg[v];
}
if(n==2) return printf(2 2n1 1n),0;
dfs(1,0);
mark(1,0,0);
printf(%d %dn,max(f[1][0],f[1][1]),f[1][0]==f[1][1]min(g[1][0],g[1][1])(f[1][0]f[1][1]g[1][0]g[1][1]));
for(int i=1;i=n;i++) printf(%d ,mrk[i]deg[i]1);
return 0;
}