[CF468D]Tree
[CF468D]Tree
题目大意:
一棵\(n(n\le10^5)\)个编号为\(1\sim n\)的点的带边权的树,求一个排列\(p_{1\sim n}\),使\(\sum dis(i,p_i)\)最大。求最大化的\(\sum dis(i,p_i)\)以及字典序最小的\(p\)。
思路:
考虑第一问。用\(dis(x)\)表示点\(x\)到根的距离。则不难发现\(\sum dis(i,p_i)=\sum(dep_i+dep_{p_i}-2\times dep_{lca(i,p_i)})=2\times\sum dep_i-2\times\sum dep_{lca(i,p_i)}\)。而如果我们能够找到一个合适的点作为根,使得\(lca(i,p_i)=1\)则答案最大值即为\(2\times\sum dep_i\)。而通过证明可以发现一个点可以作为根当且仅当这个点是树的重心,证明如下(引自Code仓库):
设\(P\)为重心,若\(P\)不可被当作公共点,设\(T_1\)是\(P\)的大小\(>\lfloor\frac n2\rfloor\)的子树,其根为\(Q\),那么把\(Q\)拔掉的话,包含\(P\)的那棵子树的大小就会\(<n−\lfloor\frac n2\rfloor=\lceil\frac n2\rceil\le\lfloor\frac n2\rfloor+1\le T_1\)的大小,并且把\(Q\)拔掉后的其他子树大小显然都会小于\(T_1\)的大小,因此把\(Q\)拔掉会让剩余的最大子树的大小比把\(P\)拔掉的还要小,则\(P\)不是重心,矛盾。因此重心可以被当作公共点。
再来证明非重心的点不能被当作公共点,一样设\(P\)为重心,并且\(Q\)不是重心,他落在\(P\)的\(T_1\)子树中,那么有\(T_1\)的大小\(\le\lfloor\frac n2\rfloor\),因此整棵树扣掉\(T_1\)的大小\(\ge\lceil\frac n2\rceil\),因此可以得到若\(Q\)想要当公共点,他就必须是\(T_1\)的根,并且满足\(T_1\)的大小刚好是\(\lfloor\frac n2\rfloor\),并且整棵树扣掉\(T_1\)的大小要刚好是\(\lceil\frac n2\rceil\),所以就可以得到\(n\)为偶数,\(T_1\)的大小为\(\frac n2\),所以\(Q\)也是重心,矛盾。
此时我们已经完成了第一问,可以解决HDU4118这个问题。现在考虑第二问,即如何求出字典序最小的\(p\)。
如果定义排列中\(i\)为出点,\(p_i\)为入点,将树上的每一个点拆成一个入点和一个出点,那么题目就变成了一个完全匹配问题。去掉重心后原图分为\(T_{1\sim r}\)共\(r\)个子树,记子树\(T_i\)中有\(in[i]\)个未匹配的入点,\(out[i]\)个未匹配的出点,显然初始状态\(in[i]=out[i]=size(T_i)\)。由于每个出点都要匹配不同一个子树的一个入点,则\(out[i]\le in[1]+\ldots+in[i-1]+in[i+1]+\ldots+in[r]\),即\(in[i]+out[i]\le\sum_{j=1}^r in[j]\),也即\(in[i]+out[i]\)小于此时未匹配的入点个数。若按\(1\sim n\)的顺序求\(p_i\),则对于每一时刻,对于每一棵子树\(T_j\),都有\(in[j]+out[j]\le n-i+1\)。
若存在子树\(T_j\),满足\(in[j]+out[j]=n-i+1\),则\(p_i\)必须在\(T_j\)中取,因为要保证字典序最小,将\(T_j\)中最小的入点作为\(p_i\)即可。若不存在这样的\(T_j\),则可以从任意一个不同于\(i\)所属子树的子树中选取最小值。
这些最小值可以通过线段树、红黑树、二叉堆等数据结构来维护。考虑使用std::set
(红黑树),用std::set<int> in[N]
维护每个子树中所有未匹配的入点编号,std::set<int> min
维护每个子树中未匹配的编号最小的入点,std::set<std::pair<int,int>> set
记录每个子树中未匹配的入点和出点总数和该子树编号。时间复杂度\(\mathcal O(n\log n)\)。
源代码:
#include<set>
#include<cstdio>
#include<cctype>
#include<climits>
#include<forward_list>
using int64=long long;
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
constexpr int N=1e5+1;
using Edge=std::pair<int,int>;
std::forward_list<Edge> e[N];
inline void add_edge(const int &u,const int &v,const int &w) {
e[u].push_front({v,w});
e[v].push_front({u,w});
}
int n,m,size[N],par[N],bel[N],max[N]={INT_MAX},sum[N],cen;
int64 dis[N],ans;
void dfs(const int &x,const int &par) {
size[x]=1;
for(auto &j:e[x]) {
const int &y=j.first;
if(y==par) continue;
dfs(y,x);
size[x]+=size[y];
max[x]=std::max(max[x],size[y]);
}
max[x]=std::max(max[x],n-size[x]);
if(max[x]<max[cen]) cen=x;
}
void dfs(const int &x) {
ans+=dis[x];
for(auto &j:e[x]) {
const int &y=j.first,&w=j.second;
if(y==par[x]) continue;
dis[y]=dis[par[y]=x]+w;
dfs(y);
}
}
std::set<int> in[N];
std::set<int> min;
std::set<std::pair<int,int>> set;
void work(const int &x,const int &root) {
in[bel[x]=root].insert(x);
for(auto &j:e[x]) {
const int &y=j.first;
if(y==par[x]) continue;
work(y,root);
}
}
inline void link(const int &x,const int &y) {
const int &p=bel[x],&q=bel[y];
min.erase(y);
if(p) {
set.erase({sum[p],p});
set.insert({--sum[p],p});
}
if(q) {
in[q].erase(y);
if(!in[q].empty()) min.insert(*in[q].begin());
set.erase({sum[q],q});
set.insert({--sum[q],q});
}
}
inline int solve(const int &x) {
int ret;
if(set.rbegin()->first==n-x+1&&set.rbegin()->second!=bel[x]) {
ret=*in[set.rbegin()->second].begin();
} else {
ret=bel[*min.begin()]!=bel[x]||x==cen?*min.begin():*std::next(min.begin());
}
link(x,ret);
return ret;
}
int main() {
for(register int i=n=getint();i>1;i--) {
const int u=getint(),v=getint();
add_edge(u,v,getint());
}
dfs(1,0);
dfs(cen);
printf("%lld\n",ans*2);
if(n==1) {
puts("1");
return 0;
}
min.insert(cen);
for(register auto &j:e[cen]) {
const int &x=j.first;
work(x,x);
min.insert(*in[x].begin());
set.insert({sum[x]=in[x].size()*2,x});
}
for(register int i=1;i<=n;i++) {
printf("%d%c",solve(i)," \n"[i==n]);
}
return 0;
}
[CF468D]Tree的更多相关文章
- [数据结构]——二叉树(Binary Tree)、二叉搜索树(Binary Search Tree)及其衍生算法
二叉树(Binary Tree)是最简单的树形数据结构,然而却十分精妙.其衍生出各种算法,以致于占据了数据结构的半壁江山.STL中大名顶顶的关联容器--集合(set).映射(map)便是使用二叉树实现 ...
- SAP CRM 树视图(TREE VIEW)
树视图可以用于表示数据的层次. 例如:SAP CRM中的组织结构数据可以表示为树视图. 在SAP CRM Web UI的术语当中,没有像表视图(table view)或者表单视图(form view) ...
- 无限分级和tree结构数据增删改【提供Demo下载】
无限分级 很多时候我们不确定等级关系的层级,这个时候就需要用到无限分级了. 说到无限分级,又要扯到递归调用了.(据说频繁递归是很耗性能的),在此我们需要先设计好表机构,用来存储无限分级的数据.当然,以 ...
- 2000条你应知的WPF小姿势 基础篇<45-50 Visual Tree&Logic Tree 附带两个小工具>
在正文开始之前需要介绍一个人:Sean Sexton. 来自明尼苏达双城的软件工程师.最为出色的是他维护了两个博客:2,000Things You Should Know About C# 和 2,0 ...
- Leetcode 笔记 110 - Balanced Binary Tree
题目链接:Balanced Binary Tree | LeetCode OJ Given a binary tree, determine if it is height-balanced. For ...
- Leetcode 笔记 100 - Same Tree
题目链接:Same Tree | LeetCode OJ Given two binary trees, write a function to check if they are equal or ...
- Leetcode 笔记 99 - Recover Binary Search Tree
题目链接:Recover Binary Search Tree | LeetCode OJ Two elements of a binary search tree (BST) are swapped ...
- Leetcode 笔记 98 - Validate Binary Search Tree
题目链接:Validate Binary Search Tree | LeetCode OJ Given a binary tree, determine if it is a valid binar ...
- Leetcode 笔记 101 - Symmetric Tree
题目链接:Symmetric Tree | LeetCode OJ Given a binary tree, check whether it is a mirror of itself (ie, s ...
随机推荐
- 在Debian9安装node和npm
这学期又快结束了,坐在每天面对的电脑面,本着整理资料.更换心情的目的,我重装了一下自己的debian.下面就将自己安装node的过程进行记录与分享. node的官网:https://nodejs.or ...
- 图论:Stoer-Wagner算法
利用Stoer-Wagner算法求无向图最小割 直接给出算法描述和过程实现: 算法步骤: . 设最小割cut=INF, 任选一个点s到集合A中, 定义W(A, p)为A中的所有点到A外一点p的权总和. ...
- YDB基本使用详解(转)
第七章YDB基本使用详解 一.如何与YDB对接(交互) 目前延云YDB提供如下几种方式 l命令行的方式 lWeb http接口的方式 lJDBC接口的方式 通过Java编程接入 通过可视化SQL分析统 ...
- 【hdu1251-统计难题】Trie
http://acm.hust.edu.cn/vjudge/problem/16379 题意:给定多个单词,多次询问符合某前缀的单词有多少个. 题解:tire.数组开了5*10^6才A,不然就RE. ...
- LCD实验学习笔记(三):WATCH DOG
看门狗是为了能够防止程序跑飞用的.程序应该定时的去喂狗.如果程序跑飞了,那么就不会去喂狗了.如果超过了喂狗的时间,那么狗就会生成一个信号来reset CPU.一般程序不需要,特殊情况下需要这种机制. ...
- 进一步认识golang中的并发
如果你成天与编程为伍,那么并发这个名词对你而言一定特别耳熟.需要并发的场景太多了,例如一个聊天程序,如果你想让这个聊天程序能够同时接收信息和发送信息,就一定会用到并发,无论那是什么样的并发. 并发的意 ...
- Codeforces 362E Petya and Pipes 费用流建图
题意: 给一个网络中某些边增加容量,增加的总和最大为K,使得最大流最大. 费用流:在某条边增加单位流量的费用. 那么就可以2个点之间建2条边,第一条给定边(u,v,x,0)这条边费用为0 同时另一条边 ...
- 一次Ubuntu下的排雷记录
起因 某天,发现一台服务器上出现了一个大量占用cpu资源的进程.尝试手动杀掉,但很快就会自动重新创建新的进程. 追查 用命令lsof -p 10316 查看其文件路径: 该进程文件夹/proc/103 ...
- cobbler部署安装
挂载光驱 # mount /dev/cdrom /mnt/ 安装cobbler所需软件 # yum install cobbler cobbler-web dhcp tftp-server pykic ...
- Android 开发实用方法大全
1.格式化价格,这个经常在计算费用精度的时候用到 /** * 格式化价格 * * @param argStr 传入价格字符串 * @return */ public static String get ...