[codeforces 1187E]ツリーペインティング换根dp
一般的なカタログについては、https://blog.csdn.net/mrcrack/article/details/105199636を参照してください
オンライン評価用アドレスhttps://codeforces.ml/problemset/problem/1187/E
問題 | ただ | 評決 | 時間 | 記憶 |
---|---|---|---|---|
1187E-木の絵画 | GNU C ++ 11 | 受け入れた | 109ミリ秒 | 13400 KB |
上記のデータを手動シミュレーションに使用します。暴力シミュレーションの方法は次のとおりです。
sz []はサブツリーのノード数を保存することに対応し、d []は各ノードサブツリーの重みの合計を保存することに対応します。これは問題です。
ルートdpアルゴリズムはhttps://blog.csdn.net/ccsu_cat/article/details/94397592と同じです
tmp = d [u] -d [v] -sz [v];
d [v] = d [v] + tmp + n-sz [v];
説明は次のとおりです。
アイデア:最初のものを設定すると、合計の重みは固定値であり、問題は次のように変換され、各ポイントをカラーリングの開始点から取得できる合計の重みとして列挙すると、答えは最大のセットになりますd [u]は、uサブツリーで最初にuポイントをペイントすることによって取得できる合計の重みであり、sz [u]はuサブツリーのサイズであり、次に伝達方程式:d [u] = d [son] + sz [u] 、最初にdfsをルートおよびカラーリングの開始点として答えを見つけ、次にdfsを使用してルートdpを変更します。uの息子vの場合、ルートをvに転送し、最初にuv接続を切断する必要があります:D [u] = d [u] -sz [v] -d [v]、次にvをルートとして接続:d [v] = d [v] + d [u] + n-sz [v] 。
n-sz [v]の説明は、http://www.machaoqiang.cn/?p = 1022から取得されます
もちろん、1をルートとして使用することは明らかに最適ではありません。ルートを変更して、uの子vをルートとして使用します。vをルートとして使用した合計スコアは、sz [v]段落よりも小さくなることがわかります。ルートノードが汚れ始めたときの最初の答えは、間違いなくnです。しかし、uをルートとするサブツリーがさらに存在します(現時点ではuはvに対する子です)。このセグメントはsz [u]で表すことはできません。合計n個のポイントがあるため、このセグメントはn-szで使用できます。 [v]は言った。
ルートdpアルゴリズムは次のようにシミュレーションされます
ACコードは以下の通りです
#include <cstdio>
#include <algorithm>
#define LL long long
#define maxn 200010
using namespace std;
int n,head[maxn],cnt;
LL sz[maxn],d[maxn],ans;//d[]保存每结点子树的权值总和 即 题目所求;sz[]保存该子树的节点数
struct node{
int to,next;
}e[maxn<<1];
void add_edge(int u,int v){//邻接表
cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
void init(){
int i,u,v;
scanf("%d",&n);
for(i=1;i<n;i++){
scanf("%d%d",&u,&v);
add_edge(u,v),add_edge(v,u);//无向图
}
}
void dfs1(int u,int fa){//以1为根节点,计算sz[],d[]
int v,b;
sz[u]=1;//每个子树的节点数初始化为1
for(b=head[u];b;b=e[b].next){
v=e[b].to;//用 v 表示 子节点
if(v==fa)continue;//如果 u 的子节点 v 为 u 的父节点 fa;(fa已经涂色) 则跳过此子节点
dfs1(v,u);//求子节点的权值d[v] 和 子树节点数sz[v]
sz[u]+=sz[v];//把每一个子树大小相加
d[u]+=d[v];//该点子节点权值 相加
}
d[u]+=sz[u];//该点权值 加 所有子节点的权值
}
void dfs2(int u,int fa){//换根dp
int v,b;
LL tmp;
ans=max(ans,d[u]);//在u节点为第一次涂色情况时的权值大小 ,取大值更新ans
for(b=head[u];b;b=e[b].next){//换根
v=e[b].to;
if(v==fa)continue;//父节点已经算过 跳过
tmp=d[u]-d[v]-sz[v];
d[v]=d[v]+tmp+n-sz[v];
dfs2(v,u);//继续向子节点扫描
}
}
int main(){
init();
dfs1(1,0);
dfs2(1,0);
printf("%lld\n",ans);
return 0;
}