codeforces 698b 图论

题意: 给一串数字a1~an,ai表示i与ai相连,问给出的数组经过最少数量的更改能形成一颗树

题解:分析可知组成的只能由三种非连通图构成,点,不含环链,含环链,含环链必定只可能还有一个环

   统计三种的数量,点和不含环链必定会有父节点为自身的点,含环链用强连通跑一跑,最后把这三种图连到一个节点上就行

   注意一下只含有含环链的情况

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cmath>
  4 #include <iostream>
  5 #include <algorithm>
  6 #include <map>
  7 #include <vector>
  8 #include <queue>
  9 using namespace std;
 10 #define EPS 1e-10
 11 typedef long long ll;
 12 const int maxn = 2e5 + 10;
 13 const int INF = 1e9 ;
 14 const int N = maxn;
 15 const int M = maxn;
 16
 17 struct Edge {
 18     int to, next;
 19 } edge[M*2];
 20 int head[N];
 21 int cnt_edge;
 22
 23 void add_edge(int u, int v)
 24 {
 25     edge[cnt_edge].to = v;
 26     edge[cnt_edge].next = head[u];
 27     head[u] = cnt_edge;
 28     cnt_edge++;
 29 }
 30 vector<int>q;
 31 int dfn[N];
 32 int low[N];
 33 int stk[N];
 34 bool in[N];
 35 int kind[N];
 36
 37 int top;
 38 int idx;
 39 int cnt;
 40
 41 int n, m,num;
 42
 43 void dfs(int u)
 44 {
 45     dfn[u] = low[u] = ++idx;
 46     in[u] = true;
 47     stk[++top] = u;
 48     for (int i = head[u]; i != -1; i = edge[i].next)
 49     {
 50         int v = edge[i].to;
 51         if (!dfn[v])
 52         {
 53             dfs(v);
 54             low[u] = min(low[v], low[u]);
 55         }
 56         else if(in[v])
 57         {
 58             low[u] = min(low[u], dfn[v]);
 59         }
 60     }
 61
 62     if (low[u] == dfn[u])
 63     {
 64         ++cnt;
 65         int j;
 66         num = 0;
 67         do {
 68             num++;
 69             j = stk[top--];
 70             in[j] = false;
 71             kind[j] = cnt;
 72         } while (j != u);
 73         if(num > 1)
 74         q.push_back(stk[top+1]);
 75     }
 76 }
 77
 78 void init()
 79 {
 80     memset(dfn, 0, sizeof dfn);
 81     memset(head, -1, sizeof head);
 82     cnt_edge = 0;
 83     top = cnt = idx = 0;
 84 }
 85 int main(){
 86    // freopen("in.txt","r",stdin);
 87     cin>>n;
 88     int a[maxn];
 89     init();
 90     int cnt = 0;
 91     for(int i = 1;i <= n;i++){
 92         scanf("%d",&a[i]);
 93         if(i != a[i]){
 94             add_edge(i,a[i]);
 95         }
 96         else{ q.push_back(i);
 97             cnt = 1;
 98         }
 99     }
100
101     for(int i = 1;i <= n;i++){
102     if(!dfn[i]){
103         dfs(i);
104     }
105     }
106
107     if(num == n){
108         cout<<1<<endl;
109         a[q[0]] = q[0];
110         for(int i = 1;i <= n;i++)
111             if(i == n) printf("%d\n",a[i]);
112             else printf("%d ",a[i]);
113     }
114     else{
115         if(cnt)
116             cout<<q.size() - 1<<endl;
117         else
118             cout<<q.size()<<endl;
119         for(int i = 0;i < q.size();i++)
120             a[q[i]] = q[0];
121         for(int i = 1;i <= n;i++){
122             if(i == n) printf("%d\n",a[i]);
123             else printf("%d ",a[i]);
124         }
125     }
126     return 0;
127 }
时间: 2024-08-06 16:04:35

codeforces 698b 图论的相关文章

【codeforces 698B】 Fix a Tree

题目链接: http://codeforces.com/problemset/problem/698/B 题解: 还是比较简单的.因为每个节点只有一个父亲,可以直接建反图,保证出现的环中只有一条路径. 然后发现,有多少个环,就需要改多少条边.然后设出现连向自己的节点为x,这些也要改边,对答案的贡献应为:$max(x-1,0)$.对于最后的根节点,有自环选一个,没自环在其他环上任选一个点就行. 1 #include<cstdio> 2 inline int min(int a,int b){re

Codeforces 723e [图论][欧拉回路]

/* 不要低头,不要放弃,不要气馁,不要慌张. 题意: 给你一个有n个点,m条边的无向图,给每条边规定一个方向,使得这个图变成有向图,并且使得尽可能多的点入度与出度相同. 输出有多少个这样的点并且输出有向图. 思路: 1.针对每个连通分支. 2.所有点入度与出度相同,显然这是欧拉回路存在的判定定理,但是欧拉回路的另外一个等价定理是所有点的度数是偶数.那如果给我们的图中的某些点是奇数度该怎么办. 3.显然原图中给的点如果度数是奇数,那么该点的入度与出度一定不相同. 4.根据握手定理,无向图中度数是

CodeForces 698B Fix a Tree

并查集,构造. 先看一下图的特殊性,按照这种输入方式,一个点的入度最多只有$1$,因此,问题不会特别复杂,画画图就能知道了. 如果给出的序列中已经存在$a[i]=i$,那么随便取一个$a[i]=i$的$i$作为$root$,剩下的每一条边$a[i] \to i$,可以用并查集来处理,如果发现某条边$a[i] \to i$加入前$a[i]$与$i$已经在同一集合中,说明再加$a[i] \to i$会导致成环,因此将$i$的$father$改成$root$即可,并将$i$与$root$合并. 如果给

CodeForces 698B Fix a Tree (并查集应用)

当时也是想到了并查集,但是有几个地方没有想清楚,所以就不知道怎么写了,比如说如何确定最优的问题.赛后看了一下别人的思路,才知道自己确实经验不足,思维也没跟上. 其实没有那么复杂,这个题目我们的操作只有三个 1.确定根节点.2.解环. 3连接子树. 如果题目中给出了一个或者多个根节点,我们任选一个即可,证明:假设有k个可行根节点,那么选择一个不动,改动k-1次,每种选择都是这样.但如果题目中没有可选根节点,就不可以随便去选了,首先明确这种情况一定存在了1个或者多个环,我们一定要从环中选取根节点,因

Codeforces 444A DZY Loves Physics(图论)

题目链接:Codeforces 444A DZY Loves Physics 题目大意:给出一张图,图中的每个节点,每条边都有一个权值,现在有从中挑出一张子图,要求子图联通,并且被选中的任意两点,如果存在边,则一定要被选中.问说点的权值和/边的权值和最大是多少. 解题思路:是图论中的一个结论,最多两个节点,所以枚举两条边就可以了.我简单的推了一下,2个点的情况肯定比3个点的优. 假设有3个点a,b,c,权值分别为A,B,C 现a-b,b-c边的权值分别为u,v 那么对于两点的情况有A+Bu,B+

CodeForces 22C System Administrator 小水怡情 图论+构造

题目链接:点击打开链接 构造一个星形图+一个完全图就好了.. #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <iostream> #include <map> #include <set> #include <math.h> using namespace std; #define inf

图论/位运算 Codeforces Round #285 (Div. 2) C. Misha and Forest

题目传送门 1 /* 2 题意:给出无向无环图,每一个点的度数和相邻点的异或和(a^b^c^....) 3 图论/位运算:其实这题很简单.类似拓扑排序,先把度数为1的先入对,每一次少一个度数 4 关键在于更新异或和,精髓:a ^ b = c -> a ^ c = b, b ^ c = a; 5 */ 6 #include <cstdio> 7 #include <cstring> 8 #include <cmath> 9 #include <algorith

图论/暴力 Codeforces Beta Round #94 (Div. 2 Only) B. Students and Shoelaces

题目传送门 1 /* 2 图论/暴力:这是个连通的问题,每一次把所有度数为1的砍掉,把连接的点再砍掉,总之很神奇,不懂:) 3 */ 4 #include <cstdio> 5 #include <cstring> 6 #include <algorithm> 7 #include <cmath> 8 using namespace std; 9 10 const int MAXN = 1e2 + 10; 11 const int INF = 0x3f3f3

Codeforces 789D Weird journey - 欧拉路 - 图论

Little boy Igor wants to become a traveller. At first, he decided to visit all the cities of his motherland — Uzhlyandia. It is widely known that Uzhlyandia has n cities connected with m bidirectional roads. Also, there are no two roads in the countr