zoukankan      html  css  js  c++  java
  • CF1324F Maximum White Subtree 题解

    CSDN同步

    原题链接

    简要题意:

    给定一棵树,每个点有黑白两种颜色;对每个节点,求出包含当前节点的连通图,使得白点数与黑点数差最小。输出这些值。

    F题也这么简单,咳咳,要是我也熬夜打上那么一场。。。可惜没时间打啊

    美国佬怎么想的,不能让比赛设置成美国的上午,那我们就是下午了;非要设置成下午,那我们就是半夜。。。

    首先,这题一看就是 ( exttt{dp}),树形 ( exttt{dp}),换根 ( exttt{dp}).

    下文中,用 ( exttt{Subtree(i)}) 表示 (i) 的子树包含的所有节点集合。

    ( exttt{father(i)}) 表示 (i) 节点的父亲。

    ( exttt{colour_i}) 表示 (i) 节点的颜色值,黑为 (-1),白为 (1).

    首先,假定 (1) 为根。

    (f_i) 表示,当前联通图包含在以 (i) 为根的子树内的答案。

    则必然存在:

    [f_i = colour_i + sum_{x in Subtree(i)} max(f_x,0) ]

    因为, (colour_i) 是必须包含的,其次是所有子树中的答案统计;负数不统计。

    下面考虑一个换根(树形) ( exttt{dp}).用 (g_i) 表示 整棵树去掉以 (i) 为根的子树后(保留 (i) 节点)的答案。

    [g_i = colour_i + max(0,g_{ exttt{father(i)}} + sum_{x in Subtree( exttt{father(i)})}^{i ot = x} max(f_x,0)) ]

    这也是显然的。

    你发现这玩意儿似乎是 (O(n^2)) 的???

    可以,你机智地发现,后面和 (f_i) 的状态转移方程长得不是一点点像!

    接着,我们来看后面的部分。

    [sum_{x in Subtree( exttt{father(i)})}^{i ot = x} max(f_x,0) = f_{ exttt{father(i)}} - max(0,f_i) - colour_{ exttt{father(i)}} ]

    把这个代入 (g) 可知:

    [g_i = colour_i + max(0,g_{ exttt{father(i)}} + f_{ exttt{father(i)}} - max(0,f_i) - colour_{ exttt{father(i)}}) ]

    然后考虑统计答案。

    你可能觉得是这样子的:

    [ans_i = f_i + max(0,g_i) ]

    可是你机智的发现,连样例都过不了!!!

    什么鬼?

    (f_i)(g_i) 都没毛病?

    (ans_i) 似乎 也没什么问题?

    你再次环顾了以下 (f)(g) 的方程。

    你机智的发现,两个函数都计算了 (colour_i).

    所以还要减掉一个!

    [ans_i = f_i + max(0,g_i - colour_i) ]

    天哪,你告诉我这个树形 dp 不会写???

    要是 (1) 年前的我,这里肯定是记忆化搜索。

    但是,我们就用树形 ( exttt{dp}) 写,怎么地!

    具体见代码。

    时间复杂度:(O(n)).

    空间复杂度:(O(n)).

    实际得分:(100pts).

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=2e5+1;
    
    inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
    	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
    
    int n,a[N],fa[N],f[N];
    int g[N],ans[N];
    vector<int>G[N]; 
    //正常套路,化树为图,随意求根,形成父亲
    
    inline void solve(int dep,int bs) { //bs 是 dep 的父亲,既方便处理父亲,也为后面的 g 做铺垫
    	fa[dep]=bs; f[dep]=a[dep]?1:-1;
    	for(int i=0;i<G[dep].size();i++)
    		if(G[dep][i]!=bs) {
    			solve(G[dep][i],dep);
    			if(f[G[dep][i]]>0) f[dep]+=f[G[dep][i]];
    		} //加上每个儿子维护的子树值即可,巧妙维护
    }
    
    inline void dfs(int dep) {
    	int x=fa[dep],t=g[x]+f[x]-(a[x]?1:-1);
    	if(f[dep]>0) t-=f[dep];
    	if(t<0) t=0; ans[dep]=f[dep]+t;
    	g[dep]=t+(a[dep]?1:-1);
    	for(int i=0;i<G[dep].size();i++)
    		if(G[dep][i]-fa[dep]) dfs(G[dep][i]);
    } //维护 g
    
    int main(){
    	n=read();
    	for(int i=1;i<=n;i++) a[i]=read();
    	for(int i=1;i<n;i++) {
    		int x=read(),y=read();
    		G[x].push_back(y);
    		G[y].push_back(x);
    	}
    	solve(1,0); g[1]=a[1]?1:-1; ans[1]=f[1];
    	for(int i=0;i<G[1].size();i++) dfs(G[1][i]);
    	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    	return 0;
    }
    
    
  • 相关阅读:
    box-shadow使用指南
    chrome的input默认样式黄色背景以及选中加粗的边框处理
    Spring AOP执行方法
    Spring JDBC主从数据库访问配置
    JS 命名冲突
    Data truncation: Truncated incorrect DOUBLE value 解决方案
    MySQL DATE_ADD() 函数
    Codeforces Round #340 (Div. 2)
    2020牛客寒假算法基础集训营3
    Codeforces Round #377 (Div. 2)
  • 原文地址:https://www.cnblogs.com/bifanwen/p/12551077.html
Copyright © 2011-2022 走看看