博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Codeforces Round #268 (Div. 1) 468D Tree(杜教题+树的重心+线段树+set)
阅读量:6698 次
发布时间:2019-06-25

本文共 4649 字,大约阅读时间需要 15 分钟。

题目大意

给出一棵树,边上有权值,要求给出一个1到n的排列p,使得sigma d(i, pi)最大,且p的字典序尽量小。

d(u, v)为树上两点u和v的距离

 

题解:一开始没看出来p需要每个数都不同,直接敲了个轻重边剖分orz,交上去才发现不对

#include 
#include
#include
#include
#define fi first#define se secondusing namespace std;typedef long long LL;typedef pair
PII;typedef pair
PLI;const int maxn = 1e5 + 100;PLI dp[maxn], dp2[maxn];vector
G[maxn];int son[maxn];void dfs(int x, int fa){ dp[x].se = x; dp[x].fi = 0; for(auto e : G[x]){ if(e.fi == fa) continue; dfs(e.fi, x); if(dp[e.fi].fi + e.se >= dp[x].fi){ if(dp[e.fi].fi + e.se == dp[x].fi){ son[x] = dp[x].se < dp[e.fi].se ? son[x] : e.fi; dp[x].se = min(dp[x].se, dp[e.fi].se); } else { son[x] = e.fi; dp[x].fi = dp[e.fi].fi + e.se; dp[x].se = dp[e.fi].se; } } }}void Maintain(LL v, PLI A, PLI& B){ if(v + A.fi > B.fi){ B.fi = v + A.fi; B.se = A.se; } else if(v + A.fi == B.fi){ B.se = B.se < A.se ? B.se : A.se; }}void ddfs(int x, int fa, LL v){ dp2[x].se = x; for(auto e : G[x]){ if(e.fi == fa) continue; if(e.fi == son[x]) continue; Maintain(e.se, dp[e.fi], dp2[x]); } if(son[fa] == x){ Maintain(v, dp2[fa], dp2[x]); } else { Maintain(v, dp[fa], dp2[x]); Maintain(v, dp2[fa], dp2[x]); } for(auto e : G[x]) if(e.fi != fa) ddfs(e.fi, x, e.se);}int main(){ int n, x, y, w; cin>>n; for(int i = 1; i < n; i++){ cin>>x>>y>>w; G[x].push_back({y, w}); G[y].push_back({x, w}); } dfs(1, 1); LL ans = 0; vector
V; ans += dp[1].fi; V.push_back(dp[1].se); dp2[1].se = 1; for(auto e : G[1]){ if(e.fi == son[1]) continue; Maintain(e.se, dp[e.fi], dp2[1]); } for(auto e : G[1]) ddfs(e.fi, 1, e.se); for(int i = 2; i <= n; i++){ if(dp[i].fi > dp2[i].fi){ ans += dp[i].fi; V.push_back(dp[i].se); } else if(dp[i].fi == dp2[i].fi){ ans += dp[i].fi; V.push_back(dp[i].se < dp2[i].se ? dp[i].se : dp2[i].se); } else { ans += dp2[i].fi; V.push_back(dp2[i].se); } } cout<
<

 

 

题解2:

如果排列要求都不同的话,实际上求最大值反而好求了

考虑在以任意点x为根,u和v匹配的答案

就是dis(x, u) + dis(x, v) - dis(x, lca(u, v))

如果这个点x是树的重心,我们就可以使得每个匹配的lca都是x,这时就可以得到最优解

所以求最优解还是很容易的

接下来是匹配

匹配实际上并不好做,需要考虑下面几个问题

1、找到树的重心以后,需要把每颗子树的dfs序用线段树维护,然后用线段树查询值最小的点

2、在匹配的过程中,有可能出现如果把u和v匹配之后,后面就无法完成匹配的情况,这时只能用u来匹配最需要匹配的那棵子树

这个理解起来可能比较抽象,写成具体的式子实际上是

令px[i]表示子树i内还有多少个点没有跟其他子树中的点匹配

py[i]表示子树i内还有多少个点没有被(强调被动)其他子树中的点匹配

如果当前还有y个点没进行匹配,那么如果存在一颗子树i, y < px[i] + py[i],实际上就必须要优先考虑i子树了。

因为如果还是按照字典序匹配,就会造成后续的i子树外需要匹配的点的个数,小于i子树内需要被匹配的点的个数,就根本无法完成匹配!

当然如果这个点本身还是在i子树内的话,那么还是可以和其他子树匹配的,(因为y和px[i]同时减1,不影响后续匹配)

3、重心可以和重心本身匹配,这很扯。。但是确实是可行的,需要特殊考虑

 

实现过程的时候,用set来维护px[i] + py[i],如果出现了y < px[i] + py[i],就进行特殊处理。

没出现就按字典序查询匹配。整个过程都要动态维护px[i], py[i]的值

然后线段树维护dfs序不再多说。

 

#include 
#include
#include
#include
#include
#define fi first#define se secondusing namespace std;const int maxn = 1e5 + 100;typedef long long LL;typedef pair
PLI;typedef pair
PII;vector
G[maxn];set
S;int son[maxn], sz[maxn], f[maxn], col[maxn], ll[maxn], rr[maxn], no[maxn], ans[maxn], M[maxn];int tree[maxn*4];int n, tot;LL ANS = 0;void dfs0(int x, int fa){ sz[x] = 1; for(auto e : G[x]){ if(e.fi == fa) continue; dfs0(e.fi, x); sz[x] += sz[e.fi]; son[x] = max(son[x], sz[e.fi]); } son[x] = max(son[x], n - sz[x]);}void dfs(int x, int fa, LL v){ sz[x] = 1; for(auto e : G[x]){ if(e.fi == fa) continue; dfs(e.fi, x, e.se+v); sz[x] += sz[e.fi]; } ANS += v;}void Insert(int o, int l, int r, int k, int v){ if(l == r) { tree[o] = v; return; } int mid = (l+r)>>1; if(k <= mid) Insert(o*2, l, mid, k, v); else Insert(o*2+1, mid+1, r, k, v); tree[o] = min(tree[o*2], tree[o*2+1]);}int Query(int o, int l, int r, int L, int R){ if(L <= l && r <= R) return tree[o]; int mid = (l+r)>>1, ans = 1e9; if(L <= mid) ans = min(ans, Query(o*2, l, mid, L, R)); if(R > mid) ans = min(ans, Query(o*2+1, mid+1, r, L, R)); return ans;}void dfs1(int x, int fa, int cc){ f[x] = ++tot; col[x] = cc; Insert(1, 1, n, tot, x); for(auto e : G[x]){ if(e.fi == fa) continue; dfs1(e.fi, x, cc); }}void Erase(int col){ PII x = make_pair(-M[col]-no[col], col); S.erase(x); if(x.fi+1 < 0) S.insert({x.fi+1, col});}int main(){ cin>>n; for(int i = 1; i < n; i++){ int x, y, w; cin>>x>>y>>w; G[x].push_back({y, w}); G[y].push_back({x, w}); } int X; dfs0(1, 1); for(int i = 1; i <= n; i++) if(son[i] <= n/2) X = i; dfs(X, X, 0); cout<
<
0) y = min(y, Query(1, 1, n, 1, l-1)); if(r+1 <= n) y = min(y, Query(1, 1, n, r+1, n)); if(f[i] == n) y = min(i, y); Insert(1, 1, n, f[y], 1e9); ans[i] = y; Erase(col[i]); no[col[i]]--; Erase(col[y]); M[col[y]]--; } for(int i = 1; i <= n; i++) cout<
<<" ";}

 

转载于:https://www.cnblogs.com/Saurus/p/7077966.html

你可能感兴趣的文章
Cassandra 的数据存储结构——本质是SortedMap<RowKey, SortedMap<ColumnKey, ColumnValue>>
查看>>
解决虚拟机时间引起的奇怪问题
查看>>
PIX525故障一例,求解
查看>>
Lync Server外部访问系列PART5:模拟公网DNS
查看>>
【Hibernate框架开发之九】Hibernate 性能优化笔记!(遍历、一级/二级/查询/缓存/乐观悲观锁等优化算法)...
查看>>
运维自动化之使用PHP+MYSQL+SHELL打造私有监控系统(一)
查看>>
[零基础学JAVA]Java SE应用部分-34.Java常用API类库
查看>>
读书笔记2013第3本:《无价》
查看>>
一个完美的导航树
查看>>
老是不中,算了算“双色球”和“3D”,全买到底要多少¥¥。。(C 代码)
查看>>
[转载]SYSCALL_DEFINE宏定义
查看>>
深入理解 C# 协变和逆变
查看>>
【Ext.Net学习笔记】01:在ASP.NET WebForm中使用Ext.Net
查看>>
VC DLL学习
查看>>
python 转 exe -- py2exe库实录
查看>>
UWP数据绑定
查看>>
python urllib模块学习笔记
查看>>
学习API HOOK,编写了一个winsock 的封包抓取程序,可免费使用;
查看>>
EasyUI中那些不容易被发现的坑——EasyUI重复请求2次的问题
查看>>
c#多线程操作界面控件的简单实现
查看>>