题目

题目背景
我的世界里,闯入两尊神:其一 炎翼鸟,其二 英陀罗

夜卧床,窗外无光,脑中却是一片光明——是 炎翼鸟 的翅膀上的火焰,这火焰点燃的太阳。光芒如此灼烈,用手遮住双眼也徒劳:光弥散在空气中,滴落在衣服上。

夜无眠。日初升。我已伸手不见五指——是 英陀罗完全体丶须佐科夫,遮天蔽日。见过它的人都离死神不远。我知道光在外面,可光透不过这套子。

我,一介凡人,何以逆天?我就在颠倒的日夜中,努力地求着生存!

题目描述
对于一棵树,给出 p i p_i pi​ 表示树上距离 i i i 最远的点。如果这样的点有多个,则 p i = − 1 p_i=-1 pi​=−1 。

请根据 { p i } \{p_i\} {pi​} 还原出任意一棵可能的树。若无解输出 Impossible 即可。

数据范围与约定
点数 n ⩽ 1 0 6 n\leqslant 10^6 n⩽106 。

思路

本题虽说是构造题,尚且不算天马行空,可做。然吾费时于 T 2 T2 T2 却无果,悲夫!

最远点,其实是大家都很熟悉的东西。然而我似乎没能很快地联想到这些东西……

经典结论:任意点的最远点集与任意直径的端点集合有交,且任意点的最远点集只含直径端点。

由前半句,若 p i ≠ − 1 p_i\ne -1 pi​=−1,对于任意直径,其端点之一必然是 p i p_i pi​,即 p i p_i pi​ 在所有直径端点的交集之中。

因为一些历史原因(原题面对部分分的描述方式)设 S = { p i ∣ i ∈ [ 1 , n ] } ∪ { − 1 } S=\{p_i\mid i\in[1,n]\}\cup\{-1\} S={pi​∣i∈[1,n]}∪{−1} 。由于直径是很重要的信息,而 S S S 与直径直接挂钩,故下面做了些分类讨论。

∣ S ∣ = 3 |S|=3 ∣S∣=3

p i p_i pi​ 在所有直径端点的交集之中,所以 ⟨ a , b ⟩ \langle a,b\rangle ⟨a,b⟩ 必然是唯一直径。若 p a ≠ b p_a\ne b pa​=b 则存在另一条直径 ⟨ a , c ⟩ \langle a,c\rangle ⟨a,c⟩,矛盾,故 p a = b p_a=b pa​=b 。同理有 p b = a p_b=a pb​=a 。

放好 p i = − 1 p_i=-1 pi​=−1 的点很简单,就是在直径 a , b a,b a,b 的中点上挂儿子,包括中点本身也是 p i = − 1 p_i=-1 pi​=−1 的点。放 p i = b p_i=b pi​=b 则需要在中点靠近 a a a 的部分挂儿子,然后 a a a 到中点的链上都是 p i = b p_i=b pi​=b 的点。对于 p i = a p_i=a pi​=a 则对称操作。

仔细思考发现,我们可以缩减到只需要两个 p i = a ( i ≠ b ) p_i=a\;(i\ne b) pi​=a(i=b) 和 p i = b ( i ≠ a ) p_i=b\;(i\ne a) pi​=b(i=a) 的点,即一个 7 7 7 个点(或 6 6 6 个点)的直径。可以画图证明这是必要的。注意,若二者的数量均为 1 1 1,那么 5 5 5 个点(或 4 4 4 个点)的直径就可以实现了,这是仅有的特例。

∣ S ∣ = 1 |S|=1 ∣S∣=1

即 p i = − 1 p_i=-1 pi​=−1 恒成立。不难想到构造菊花图。

∣ S ∣ = 2 |S|=2 ∣S∣=2

知道直径的一端是 a a a,另一端至少有 2 2 2 个选择 b , c b,c b,c 。

若 p b ≠ a p_b\ne a pb​=a 则存在直径 ⟨ b , d ⟩ ( d ≠ a ) \langle b,d\rangle\;(d\ne a) ⟨b,d⟩(d=a),与 p i = a p_i=a pi​=a 为任意直径的端点之一矛盾。同理,必有 p b = p c = a p_b=p_c=a pb​=pc​=a 。

从小处着笔。比如就让 dis ( b , c ) = 2 \text{dis}(b,c)=2 dis(b,c)=2,设二者之间的点是 ν \nu ν,设直径长度为 5 5 5 个点。画图可见, ν \nu ν 与其子树(不含 a a a 的部分)都是 p = a p=a p=a 的点,除此之外都是 p = − 1 p=-1 p=−1 的点。

但我们需要三个 p = a p=a p=a 的点,因为有 p ν = a p_{\nu}=a pν​=a 存在。试试证明其必要性:以 a a a 为根,记 κ \kappa κ 为 b , c b,c b,c 的最近公共祖先,必然有 dis ( κ , a ) > dis ( κ , b ) = dis ( κ , c ) \text{dis}(\kappa,a)>\text{dis}(\kappa,b)=\text{dis}(\kappa,c) dis(κ,a)>dis(κ,b)=dis(κ,c),否则 ⟨ b , c ⟩ \langle b,c\rangle ⟨b,c⟩ 将成为直径。若 p κ ≠ a p_\kappa\ne a pκ​=a,则 κ \kappa κ 存在一个最远点 w ( w ∉ { a , b , c } ) w\;(w\notin\{a,b,c\}) w(w∈/{a,b,c}),因最远点总是直径端点,且 p i = a p_i=a pi​=a 是所有直径的公共端点,所以 ⟨ w , a ⟩ \langle w,a\rangle ⟨w,a⟩ 是一条直径,类似于 b , c b,c b,c 有 p w = a p_w=a pw​=a 。

所以,要么 p w = a p_w=a pw​=a,要么 p κ = a p_{\kappa}=a pκ​=a,总是能找到三个 p i = a p_i=a pi​=a 的点。

当然,我们还需要一个 p = − 1 p=-1 p=−1 的点(除 p a = − 1 p_a=-1 pa​=−1 以外),但其必要性显然:考虑 a a a 往 κ \kappa κ 方向走一步的点,不可能有 p i = a p_i=a pi​=a 。

综述

基本上都是从小处着眼,设计一个长度较小的直径即可。算是良心构造题了,只是麻烦而已。

代码

#include <cstdio> // JZM yydJUNK!!!
#include <iostream> // XJX yyds!!!
#include <algorithm> // XYX yydLONELY!!!
#include <cstring> // (the STRONG long for LONELINESS)
#include <cctype> // ZXY yydSISTER!!!
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){int a = 0, c = getchar(), f = 1;for(; !isdigit(c); c=getchar())if(c == '-') f = -f;for(; isdigit(c); c=getchar())a = (a<<3)+(a<<1)+(c^48);return a*f;
}const int MAXN = 1000006;
int p[MAXN], a, b, fa[MAXN];int main(){int n = readint();rep(i,1,n) if((p[i] = readint()) != -1){if(a == p[i] || b == p[i]) continue;if(b){ puts("Impossible"); return 0; }if(a) b = p[i]; else a = p[i];}if(n <= 3){ // must be a chainif(n == 1){puts(p[1] == 1 ? "Possible" : "Impossible");return 0;}if(a && b && p[a] == b && p[b] == a && (n != 3 || p[a^b] == -1)){puts("Possible");if(n == 3) printf("%d %d\n%d %d\n",a,a^b,b,a^b);else puts("1 2"); // n == 2}else puts("Impossible");return 0; // case closed}if(!a){ // p_i = -1puts("Possible");rep(i,2,n) printf("1 %d\n",i);}else if(!b){ // |S| = 2if(n == 4 || p[a] != -1){puts("Impossible"); return 0;}int top = a, cnt = 0, len = 0;rep(i,1,n) if(p[i] == a) b = i, ++ cnt;if(cnt < 3 || cnt == n-1){puts("Impossible"); return 0;}rep(i,1,n) if(i != a && i != b){if(p[i] == a) fa[i] = b;else if(len == 2) fa[i] = top;else fa[top] = i, ++ len, top = i;}fa[top] = b; // linkputs("Possible");rep(i,1,n) if(i != b)printf("%d %d\n",i,fa[i]);}else{ // |S| = 3if(p[a] != b || p[b] != a){puts("Impossible"); return 0;}int top[2] = {a,b}, len[2] = {0,0};rep(i,1,n) if(i != a && i != b && p[i] != -1){const int bel = (p[i] == a);if(len[bel] == 2) fa[i] = top[bel];else fa[top[bel]] = i, ++ len[bel], top[bel] = i;}int lst = 0; // chain of (p_i = -1)rep(i,1,n) if(!(~p[i])) // get anus!(lst) ? (fa[i] = lst) : (lst = i);if(len[0] && len[0] == len[1]){if(lst) fa[top[0]] = lst, fa[lst] = top[1];else fa[top[0]] = top[1]; // linkputs("Possible");rep(i,1,n) if(i != top[1])printf("%d %d\n",i,fa[i]);}else puts("Impossible");}return 0;
}

[ACNOI2022]树上构造相关推荐

  1. CF650E Clockwork Bomb(树上构造类问题、并查集)

    Description 给出两棵 n 结点的有标号树. 每次操作删去第一棵树的一条边,再加上一条边,需要保证此时还是一棵树. 构造一种操作序列,将第一棵树变成第二棵树,使得操作数最小. n ≤ 5×1 ...

  2. CF1042F Leaf Sets (贪心+树上构造)

    题目大意:给你一棵树,让你对叶节点分组,保证每组中,任意两个叶节点之间的距离不大于K,求最小的组数 手动yy的贪心竟然对的 对于每个节点,维护一个$ma[i]$,表示在$i$节点的子树内 未被分组的叶 ...

  3. python | 关键词快速匹配检索小工具 pyahocorasick / ahocorapy

    AC自动机是多模式匹配的一个经典数据结构,原理是和KMP一样的构造fail指针,不过AC自动机是在Trie树上构造的,但原理是一样的.官方github: https://github.com/Wojc ...

  4. 曼哈顿距离最小生成树莫队算法

    参考资料:https://www.cnblogs.com/CsOH/p/5904430.html https://blog.csdn.net/huzecong/article/details/8576 ...

  5. 字符串匹配 (KMP)

    文章目录 字符串匹配 字符串匹配暴力 kmp算法 next[i]数组的实现 kmp算法实现 kmp模板 字符串匹配 在常见的字符串相关的算法问题中有一种较为常见的是匹配问题,分为单模匹配与多模匹配, ...

  6. ZAFU_2021_2_17_2021寒假个人赛第四场题解

    A题 原题链接:https://codeforces.com/problemset/problem/1296/A 相关tag:简单思维 我们每次操作可以把数组中的一个数变为另一个数,那么如果这两个数同 ...

  7. HDU 2222(AC自动机模板)

    AC自动机这个算法网上有很多资料,这里就不多赘述了. 当从一个字符串中查找另一个字符串,我们有快速的算法KMP. 现在的问题是要从一个字符串中查找很多字符串,或者要从多个字符串里分别查找很多字符串.A ...

  8. [ACNOI2022]学数学不如学构造

    题目 题目描述 有一个无穷大无向图 G=(V,E)G=(V,E)G=(V,E),其中 V=N+V=\N^+V=N+ 而 E={(x,y)∣gcd⁡(x,y)≠1}E=\{(x,y)\mid \gcd( ...

  9. HDU 5385 The path(贪心、构造、最短路径树)

    HDU 5385 题目大意:给定一个图,dis表示第i个点到1点的最短路,dis1=0,给有向图上的边赋权值(1~n)满足dis1<dis2<dis3<--<disk>d ...

最新文章

  1. 【新星计划】Linux命令行相关指令汇总
  2. 【转载】Kafka介绍及升级经验分享
  3. 根文件系统构建(Yocto方式)
  4. L1-047 装睡-PAT团体程序设计天梯赛GPLT
  5. Javascript:json删除键为指定数据的值
  6. 【bzoj 3433】{Usaco2014 Jan} Recording the Moolympics(算法效率--贪心)
  7. 智鼎在线测评是测什么_人才测评工具和人才测评方法
  8. py的征途2之简例分享
  9. 耗时30分钟C++制作象棋程序,网友:优秀啊!
  10. javascript:void(0)的作用
  11. 国际清算银行:多国央行进行CBDC研究 仅少数推出具体计划
  12. 面试题:fail-safe 机制与 fail-fast 机制分别有什 么作用
  13. 计算机junit测试类,复利计算器4.0之再遇JUnit
  14. 数据防泄露产品分析选型指南
  15. 微信PC电脑桌面端多账号登录微信多开方法
  16. element ui背景图_vue+element-ui如何为元素设置背景图片
  17. 瑞士苏黎世联邦理工学院计算机专业,苏黎世联邦理工学院计算机专业
  18. 圆形比例分布图怎么做_使用PPT制作环形比例图的方法
  19. VQA(图像问答)数据集结构及大致内容
  20. ztree调用的例子(复选框、checkbox)

热门文章

  1. redis未授权访问致远程植入挖矿脚本
  2. java sscanf_【转】sscanf函数用法实例
  3. 机器人导论(第四版)学习笔记——第四章
  4. 如何利用SOLIDWORKS制作螺丝
  5. JavaScript基础学习——基础(三)
  6. 十、Openpyxl自定义单元格样式
  7. SRAM、DRAM;SDRAM、DDRSDRAM(DDR)、RDRAM;SARAM、DARAM的区别
  8. Node.js 爬虫批量下载美剧 from 人人影视 HR-HDTV
  9. 电脑蓝屏了怎么办修复,蓝屏修复方法
  10. Windows Server 2008 R2域控组策略设置禁用USB