[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 ...
随机推荐
- bzoj1862: [Zjoi2006]GameZ游戏排名系统
Description GameZ为他们最新推出的游戏开通了一个网站.世界各地的玩家都可以将自己的游戏得分上传到网站上.这样就可以看到自己在世界上的排名.得分越高,排名就越靠前.当两个玩家的名次相同时 ...
- HDU2546饭卡---(DP 经典背包)
http://acm.hdu.edu.cn/showproblem.php?pid=2546 饭卡 Time Limit: 5000/1000 MS (Java/Others) Memory L ...
- Python基础(2)_if、for、while流程控制
一.流程控制 1.条件语句 1.1单分支 1.2多分支 条件判断 计算机之所以能做很多自动化的任务,因为它可以自己做条件判断. a = 5 if a > 2: print('yes') 根据Py ...
- mysql 等 null 空值排序
[sqlserver]: sqlserver 认为 null 最小. 升序排列:null 值默认排在最前. 要想排后面,则:order by case when col is null then 1 ...
- python函数篇:名称空间、作用域和函数的嵌套
一.名称空间:(有3类) (1)内置名称空间(全局作用域) (2)全局名称空间(全局作用域) (3)局部名称空间(局部作用域) 关于名称空间的查询: x=1 def func(): print('fr ...
- [Leetcode Week6]Reorder List
Reorder List 题解 原创文章,拒绝转载 题目来源:https://leetcode.com/problems/reorder-list/description/ Description G ...
- boost::function的简单实现
前言 boost::function和boost:bind是一对强大的利器.相信用过的童鞋多少有些体会. 虽然平时在用boost::function,但是用的时候心中总会一些不安,因为不知道它是怎么实 ...
- 解决:centos7.3 tomcat7启动巨慢问题
目前公司大部分服务器操作系统还是centos6.5,tomcat用的是7,平时基本上没什么问题,启动也比较快,但是,最近有部分项目服务器更新至centos7.3 ,有些机器启动tomcat的时候巨慢无 ...
- Centos下yum update与yum upgrade的区别
转载来源于:http://www.cnblogs.com/EasonJim/p/9026357.html 说明:生产环境对软件版本和内核版本要求非常精确,别没事有事随便的进行yum update操作! ...
- JavaScript的for循环语句
语法格式 for(初始化值;循环的条件;每一次循环的递增值){ // 循环的条件结果为true,则执行循环体中的代码 } 示例(打印出1-10之间的整数): for(var a=1;a<=10; ...