@codeforces - 1205D@ Almost All
@description@
给定一个 n 个点的无向树。
请在每条边上写上权值,使得对于每一个满足 1 <= x <= \(\lfloor\frac{2*n^2}{9}\rfloor\) 的 x,存在一对 (i, j) 使得 i, j 的距离等于 x。
保证解总是存在。
Input
第一行包含一个整数 n (1≤n≤1000) 表示结点数。
接下来 n-1 行每行两个整数 u, v (1≤u,v≤n, u≠v),表示树上有一条边 (u, v)。
Output
输出 n-1 行,每行形如 u v x (0≤x≤10^6),表示 (u, v) 这条边的边权定为 x。
可以按任意顺序输出每条边的权值。
Examples
Input
3
2 3
2 1
Output
3 2 1
1 2 2
Input
4
2 4
2 3
2 1
Output
4 2 1
3 2 2
1 2 3
Input
5
1 2
1 3
1 4
2 5
Output
2 1 1
5 2 1
3 1 3
4 1 6
@solution@
看到分母中含有因子 3,又看到了这是棵树,会想到什么。
反正我是想到了边分治的时间复杂度中有个什么分母的因子有 3,于是就着手从边分治的角度开始构造。
先考虑几种比较特殊的树:
对于菊花图(即除了根以外所有点都是根的儿子),此时路径长度要么 = a[x],要么 = a[x] + a[y]。
通过两个数相加得到 O(n^2) 范围的数,可以联想到类似于 “大步小步” 的方法。
将根的儿子分为两半,一半连根的长度为 1, 2, ..., n/2,另一半连根的长度为 1*(n/2), 2*(n/2), ... (n/2)*(n/2)。可以直观地得到这样的构造是合法的。
对于链,我们同样是考虑类似于 “大步小步” 的方法,取链的中点将链分为两半,左半段所有边权值为 1,右半段所有边权值为 n/2。则所有跨越链中点的路径就可以满足题意的限制。
上面两个特例,都有两点相似的地方:将边集分为两部分;通过类 “大步小步” 的方法构造权值。
注意到 2/9 = 2/3*1/3,我们是否可以对于任意的树,将边集分为 2/3 与 1/3 的两部分,然后再通过上述方法构造权值呢?
如果使用我在开头所说的边分治中重构树的方法,是可以做到的。
我们考虑这样重构:对于点 x,假如它含有儿子 p1, p2, ... pk,我们建 k 个虚点 q1, q2, ... qk,并建虚边连成 x -> q1 -> q2 -> ... -> qk;之后,我们再建 k 条实边,第 i 条连 qi -> pi。实边 i 的权值对应着原图 x -> pi 的权值。
然后我们找一条边 e,使得 e 的左右两端连接的实点数量最接近。注意按上述方法重构出来的树才能保证两部分的数量最大差异为 1/3 与 2/3,因为实点的度数最大为 2(如果其他方法可能会出现比 1/3 小 1、比 2/3 大 1 的极端情况)。
怎么分配实边的边权呢?可以考虑从 e 的两端进行 dfs,维护当前已经经过了多少实点 cnt 与当前结点到根的路径上边权和 sum。
假如遇到一个实边,则分配 cnt - sum 的边权给实边即可。如果是 “大步” 部分就多乘一个系数(即 “小步” 部分的 siz)即可。
但是这种构造方法有一些细节,比如我 dfs 时在一个虚点,则尽量先往父亲那边跑(因为虚点到父亲点之间的路径没有任何实边);以及 e 是实边时,要给 e 分配 1 的边权;以及当我 dfs 到一个点如果没有经过任何实边,则不应该将这个点算入 cnt。
至于以上实现的正确性,其实是要分类讨论讨论出来的(可能有更细节的地方,参见代码)。
@accepted code@
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 2000;
struct Graph{
struct edge{
int to, val, tag;
edge *nxt, *rev;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt;
Graph() {ecnt = &edges[0];}
void addedge(int u, int v, bool flag = true) {
edge *p = (++ecnt), *q = (++ecnt);
p->to = v, p->tag = flag, p->nxt = adj[u], adj[u] = p;
q->to = u, q->tag = flag, q->nxt = adj[v], adj[v] = q;
p->rev = q, q->rev = p;
// printf("! %d %d %d\n", u, v, flag);
}
}G1, G2;
int fa[MAXN + 5], n, m;
void rebuild(int x, int f) {
int lst = x; fa[x] = f;
for(Graph::edge *p=G1.adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
rebuild(p->to, x);
int nw = (++m);
G2.addedge(lst, nw, false);
G2.addedge(nw, p->to);
lst = nw;
}
}
int siz[MAXN + 5];
void update(Graph::edge *&a, Graph::edge *b, int tot) {
if( a == NULL ) a = b;
else if( b != NULL && max(siz[a->to], tot-siz[a->to]) > max(siz[b->to], tot-siz[b->to]) )
a = b;
}
Graph::edge *get_mid_edge(int x, int f, int tot) {
Graph::edge *ret = NULL; siz[x] = (x <= n);
for(Graph::edge *p=G2.adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
update(ret, get_mid_edge(p->to, x, tot), tot);
update(ret, p, tot);
siz[x] += siz[p->to];
}
return ret;
}
int cnt, type, tag;
void dfs(int x, int f, bool flag, int k) {
if( x <= n && (!flag) ) cnt++;
for(Graph::edge *p=G2.adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
if( !p->tag && p->to < x ) dfs(p->to, x, flag, k);
}
for(Graph::edge *p=G2.adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
if( p->tag ) {
p->val = p->rev->val = (cnt - k)*type;
dfs(p->to, x, false, cnt);
}
else if( !p->tag && p->to > x ) dfs(p->to, x, flag, k);
}
}
int main() {
scanf("%d", &n); m = n;
for(int i=1;i<n;i++) {
int u, v; scanf("%d%d", &u, &v);
G1.addedge(u, v);
}
rebuild(1, 0);
Graph::edge *e = get_mid_edge(1, 0, n);
if( e ) {
if( e->tag ) e->val = e->rev->val = 1;
type = 1, cnt = 1, dfs(e->to, e->rev->to, true, 0);
type = siz[e->to], cnt = 1, dfs(e->rev->to, e->to, true, 0);
}
for(int i=2;i<=n;i++)
for(Graph::edge *p=G2.adj[i];p;p=p->nxt)
if( p->tag ) printf("%d %d %d\n", i, fa[i], p->val);
}
@details@
虽然官方题解不是这个,但我觉得,使用边分治重构树的技巧进行构造还是很有趣的。
只是细节超级多,试了好久才勉强写出来一个比较简洁的正确代码。
@codeforces - 1205D@ Almost All的更多相关文章
- python爬虫学习(5) —— 扒一下codeforces题面
上一次我们拿学校的URP做了个小小的demo.... 其实我们还可以把每个学生的证件照爬下来做成一个证件照校花校草评比 另外也可以写一个物理实验自动选课... 但是出于多种原因,,还是绕开这些敏感话题 ...
- 【Codeforces 738D】Sea Battle(贪心)
http://codeforces.com/contest/738/problem/D Galya is playing one-dimensional Sea Battle on a 1 × n g ...
- 【Codeforces 738C】Road to Cinema
http://codeforces.com/contest/738/problem/C Vasya is currently at a car rental service, and he wants ...
- 【Codeforces 738A】Interview with Oleg
http://codeforces.com/contest/738/problem/A Polycarp has interviewed Oleg and has written the interv ...
- CodeForces - 662A Gambling Nim
http://codeforces.com/problemset/problem/662/A 题目大意: 给定n(n <= 500000)张卡片,每张卡片的两个面都写有数字,每个面都有0.5的概 ...
- CodeForces - 274B Zero Tree
http://codeforces.com/problemset/problem/274/B 题目大意: 给定你一颗树,每个点上有权值. 现在你每次取出这颗树的一颗子树(即点集和边集均是原图的子集的连 ...
- CodeForces - 261B Maxim and Restaurant
http://codeforces.com/problemset/problem/261/B 题目大意:给定n个数a1-an(n<=50,ai<=50),随机打乱后,记Si=a1+a2+a ...
- CodeForces - 696B Puzzles
http://codeforces.com/problemset/problem/696/B 题目大意: 这是一颗有n个点的树,你从根开始游走,每当你第一次到达一个点时,把这个点的权记为(你已经到过不 ...
- CodeForces - 148D Bag of mice
http://codeforces.com/problemset/problem/148/D 题目大意: 原来袋子里有w只白鼠和b只黑鼠 龙和王妃轮流从袋子里抓老鼠.谁先抓到白色老鼠谁就赢. 王妃每次 ...
随机推荐
- Python操作数据库遇到的问题
网上教程很多,不多赘述,记录一下遇到的问题. 开始安装的是Python3.x最新版本,用的是pycharm,教程参考有 https://www.cnblogs.com/yufeihlf/p/60041 ...
- Slackware网卡配置文件和配置工具
Slackware 有关网卡的配置文件是/etc/rc.d/rc.inet1.conf , 这个文件包括乙太网接口的网卡和无线网卡的配置.Slackware 还是比较纯净的,网络配置也较简单:在Sla ...
- Quick BI取数模型深度剖析
开发图表最关键的点在于选择准确的图表类型展示准确的数据,而准确的数据往往依赖于一个强大的取数模型,因此设计一个好的取数模型不仅可以解决数据安全的问题,更可以帮助每个访问者高效触达自己想要的数据,开发者 ...
- PHP 学习1.1
1 链接mysql 数据简单测试 <html><head> <title></title> <meta http-equiv="Co ...
- IDEA 创建文件夹总默认根节点问题解决
上面是文件夹结构显示,如果勾掉,就是按层级显示,空目录不会自动折叠成一行 原文地址;https://blog.csdn.net/huangjunwei6/article/details/7150755 ...
- Redis 扫盲
Redis扫盲 非关系型数据库分类: 键值存储数据库:主要会使用到一个 Hash 表,这个表有一个特定的键和一个指针指向特定的数据,Redis 列存储数据库:应对分布式存储的海量数据,键仍然存在,但是 ...
- CodePlus2017 12月月赛 div2火锅盛宴
当时看到这道题感觉真是难过,我数据结构太弱啦. 我们来看看需要求什么: 1.当前熟了的食物的最小id 2.当前熟了的食物中有没有编号为id的食物 3.当前没熟的食物中有没有编号为id的食物 4.当前没 ...
- Python PEP8标准
1.编码 如无特殊情况,文件一律使用utf-8编码 如无特殊情况,文件头部必须添加# -*- coding:utf-8 -*- 标志 2.代码 统一使用四个空格缩进 每行代码不超过80个字符 自然语言 ...
- mysql存储过程小解
mysql 存储过程 1.创建语法 delimiter $$ --$$表示改变默认的分隔符,代表以下为存储过程,不然会以SQL的方式执行 drop procedure if exists pro_na ...
- 【JZOJ4715】【NOIP2016提高A组模拟8.19】树上路径
题目描述 给出一棵树,求出最小的k,使得,且在树中存在路径p,使得k>=S且k<=E.(k为路径p上的边的权值和) 输入 第一行给出N,S,E.N代表树的点数,S,E如题目描述. 下面N- ...