[补题/研究] BUPT冬季训练Div.1 #1C: CodeForces - 699D Fix a Tree
2018-02-22 19:07
447 查看
Winter Training Div.1 #1 C题
一棵树是一个无向无环的连通图。
Let’s consider a rooted undirected tree with nn vertices, numbered 11 through nn. There are many ways to represent such a tree. One way is to create an array with nn integers p1, p2, ..., pnp1, p2, ..., pn, where pipi denotes a parent of vertex ii (here, for convenience a root is considered its own parent).
For this rooted tree the array pp is [2, 3, 3, 2][2, 3, 3, 2].
Given a sequence p1, p2, ..., pnp1, p2, ..., pn, one is able to restore a tree:
There must be exactly one index rr that pr = rpr = r. A vertex rr is a root of the tree.
For all other n − 1n − 1 vertices ii, there is an edge between vertex ii and vertex pipi.
A sequence p1, p2, ..., pnp1, p2, ..., pn is called valid if the described procedure generates some (any) rooted tree. For example, for n = 3n = 3 sequences (1,2,2),(2,3,1)(1,2,2),(2,3,1) and (2,1,3)(2,1,3) are not valid.
You are given a sequence a1, a2, ..., ana1, a2, ..., an, not necessarily valid. Your task is to change the minimum number of elements, in order to get a valid sequence. Print the minimum number of changes and an example of a valid sequence after that number of changes. If there are many valid sequences achievable in the minimum number of changes, print any of them.
The second line contains nn integers a1, a2, ..., an(1 ≤ ai ≤ n)a1, a2, ..., an(1 ≤ ai ≤ n).
In the second line, print any valid sequence possible to get from (a1, a2, ..., an)(a1, a2, ..., an) in the minimum number of changes. If there are many such sequences, any of them will be accepted.
Examples
In the second sample, the given sequence is already valid.
1.自环x=pre[x]x=pre[x]
2.类似这样
113→→714↓18→→6↑8↑15→←4→12←5↓2↑111→7→6→4→5↑↓13→148←12←2↓↑↑18→1511
不可能有长成其他样子的环。
那么我们可以利用父亲只有一个的特点,去发现这样破坏性质的环,然后将其修正。
1.自环很好判断:如果原始状态下,x=pre[x]x=pre[x],那么它必然是要纳入判断的。
2.其他环也很好判断:
对于并查集的合并,我们取一个例子会发现:
实际上最终也会压缩到环上一点,那么我们把这样的结点作为环的代表即可。
但是,这样并查集的find必须改成非递归形式。因为递归是先递后归,不找到原始即为自环的结点不罢休,但是非自环的环会使得递归陷入死循环。
这两种x=pre[x]x=pre[x]的区别就是后者实际上在find(x)的途中已经访问过。
时间复杂度:因为find在每个结点上只会判断一次,所以是O(n)O(n),可能的环最多有n个,每个也只判断一次,所以是O(n)O(n),所以时间复杂度最坏也是O(n)O(n).
测验:空间占用5400KB,时间耗费108ms,相当优秀.
$$
D. Fix a Tree
A tree is an undirected connected graph without cycles.一棵树是一个无向无环的连通图。
Let’s consider a rooted undirected tree with nn vertices, numbered 11 through nn. There are many ways to represent such a tree. One way is to create an array with nn integers p1, p2, ..., pnp1, p2, ..., pn, where pipi denotes a parent of vertex ii (here, for convenience a root is considered its own parent).
For this rooted tree the array pp is [2, 3, 3, 2][2, 3, 3, 2].
Given a sequence p1, p2, ..., pnp1, p2, ..., pn, one is able to restore a tree:
There must be exactly one index rr that pr = rpr = r. A vertex rr is a root of the tree.
For all other n − 1n − 1 vertices ii, there is an edge between vertex ii and vertex pipi.
A sequence p1, p2, ..., pnp1, p2, ..., pn is called valid if the described procedure generates some (any) rooted tree. For example, for n = 3n = 3 sequences (1,2,2),(2,3,1)(1,2,2),(2,3,1) and (2,1,3)(2,1,3) are not valid.
You are given a sequence a1, a2, ..., ana1, a2, ..., an, not necessarily valid. Your task is to change the minimum number of elements, in order to get a valid sequence. Print the minimum number of changes and an example of a valid sequence after that number of changes. If there are many valid sequences achievable in the minimum number of changes, print any of them.
Input
The first line of the input contains an integer n(2 ≤ n ≤ 200 000)n(2 ≤ n ≤ 200 000) — the number of vertices in the tree.The second line contains nn integers a1, a2, ..., an(1 ≤ ai ≤ n)a1, a2, ..., an(1 ≤ ai ≤ n).
Output
In the first line print the minimum number of elements to change, in order to get a valid sequence.In the second line, print any valid sequence possible to get from (a1, a2, ..., an)(a1, a2, ..., an) in the minimum number of changes. If there are many such sequences, any of them will be accepted.
Examples
Input
4 2 3 3 4
Output
1 2 3 4 4
Input
5 3 2 2 5 3
Output
0 3 2 2 5 3
Input
8 2 3 5 4 1 6 6 7
Output
2 2 3 7 8 1 6 6 7
Note
In the first sample, it’s enough to change one element. In the provided output, a sequence represents a tree rooted in a vertex 44 (because p4 = 4p4 = 4), which you can see on the left drawing below. One of other correct solutions would be a sequence 2 3 3 22 3 3 2, representing a tree rooted in vertex 33 (right drawing below). On both drawings, roots are painted red.In the second sample, the given sequence is already valid.
题解
好的,我们来看一看给定数组下会破坏树的性质的地方有可能长什么样吧:1.自环x=pre[x]x=pre[x]
2.类似这样
113→→714↓18→→6↑8↑15→←4→12←5↓2↑111→7→6→4→5↑↓13→148←12←2↓↑↑18→1511
不可能有长成其他样子的环。
那么我们可以利用父亲只有一个的特点,去发现这样破坏性质的环,然后将其修正。
1.自环很好判断:如果原始状态下,x=pre[x]x=pre[x],那么它必然是要纳入判断的。
2.其他环也很好判断:
对于并查集的合并,我们取一个例子会发现:
find(7) 1→7→6→4→5 ↑ ↓ 8←2 ↑ 10 ->find(6) 1→7→4→4→5 ↑ ↑ ↓ 6 8←2 ↑ 10 ->find(4) 1→7→5←5←5←6 ↑ ↓ 4←8←2 ↑ 10 ->find(5) 5 10 ↓ ↓ 1→7→2←2←6 ↑ ↓ 4←8 ->find(2) ->find(8) ->pre[all]=4
实际上最终也会压缩到环上一点,那么我们把这样的结点作为环的代表即可。
但是,这样并查集的find必须改成非递归形式。因为递归是先递后归,不找到原始即为自环的结点不罢休,但是非自环的环会使得递归陷入死循环。
这两种x=pre[x]x=pre[x]的区别就是后者实际上在find(x)的途中已经访问过。
时间复杂度:因为find在每个结点上只会判断一次,所以是O(n)O(n),可能的环最多有n个,每个也只判断一次,所以是O(n)O(n),所以时间复杂度最坏也是O(n)O(n).
测验:空间占用5400KB,时间耗费108ms,相当优秀.
$$
#include <cstdio> #include <vector> #define F(_i,_u) for(int _i=0;_i<(_u);_i++) #define FF(_i,_l,_r) for(int _i=_l;_i<=(_r);_i++) #define FS(_i,_r,_l) for(int _i=_r;_i>=(_l);_i--) #define TRV(_i,_V) for(int _i=(_V).size()-1;_i+1;_i--) #define PB push_back #define all(x) x.begin(),x.end() using namespace std; //BigCycle:原始状态下成环的环上结点,Loop:原始状态下成自环的节点 vector<int> BigCycle,Loop; //vis:是否访问过,added是否已经加入待修改数组中 bool vis[200005]={0},added[200005]={0}; //pre:prev该结点的父亲下标,a:题目所给数组 int pre[200005],a[200005]; inline int find(int x){ int t; for(;;){ if(x==pre[x]){ //发现被压缩节点变成根节点 if(!added[x]){ if(!vis[x])Loop.PB(x); else BigCycle.PB(x); added[x]=1; } //这个vis不能放在前面,因为判断Loop和BigCycle要用到这个属性 vis[x]=1; return x; } vis[x]=1; //压缩 t=pre[x]; pre[x]=pre[t]; x=t; } } int main(){ //cg:change,改变的父亲数目,Ances:ancestor,"钦定"的符合题意祖宗 int n,cg=0,Ances; scanf("%d",&n); FF(i,1,n){ scanf("%d",&a[i]); pre[i]=a[i]; } FF(i,1,n)if(!vis[i])find(i); if(BigCycle.size())Ances=BigCycle[0]; if(Loop.size())Ances=Loop[0],cg--; TRV(i,BigCycle)a[BigCycle[i]]=Ances,cg++; TRV(i,Loop)a[Loop[i]]=Ances,cg++; printf("%d\n",cg); FF(i,1,n-1)printf("%d ",a[i]);printf("%d",a ); }
相关文章推荐
- Codeforces 204 A (五一训练 A)+想法题
- 疾病研究:杜氏进行性肌营养不良患者关节拉伸训练指南
- SEU寒假训练题解二 A Codeforces 461A
- 假期训练——CodeForces - 742D Arpa's weak amphitheater and Mehrdad's valuable Hoses DP+dfs
- CodeForces 705B (训练水题)
- SEU寒假训练题解二 B Codeforces 256C
- 字符识别OCR研究一(模板匹配&BP神经网络训练)
- 2016 北邮暑期训练3-D题(Codeforces 698A Vacations)DP,水题
- 2016 北邮暑期训练3-A题(CodeForces 697B Barnicle)白痴题
- 算法训练 Beaver's Calculator/codeforces 207A1 排序+贪心
- Codeforces-298A 思维训练
- Caffe图片训练分类研究、深度学习图片分类
- DP训练 Codeforces 816E Karen And SuperMarket [树形DP]
- C语言综合研究与高强度程序设计训练 2
- Codeforces-298B 思维训练
- Codeforces 699D Fix a Tree 并查集
- 2016 北邮暑期训练3-C题(CodeForces 699B One Bomb)白痴题
- 【caffe源码研究】第四章:完整案例源码篇(2) :LeNet初始化训练网络
- Codeforces-297A 思维训练
- 【CodeForces 312B】BUPT 2015 newbie practice #3A Archer