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$合并。

如果给出的序列中不存在$a[i]=i$,和上面的处理方法类似,唯一不同的是:找到第一条不能加入的边$a[i] \to i$,将$i$的$father$改为$i$,并且$root$设置为$i$,之后出现不能加入的边和上面处理方法一样。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
typedef long long LL;
const double pi=acos(-1.0),eps=1e-8;
void File()
{
    freopen("D:\\in.txt","r",stdin);
    freopen("D:\\out.txt","w",stdout);
}
template <class T>
inline void read(T &x)
{
    char c = getchar(); x = 0;while(!isdigit(c)) c = getchar();
    while(isdigit(c)) { x = x * 10 + c - ‘0‘; c = getchar();  }
}

const int maxn=200010;
int f[maxn],a[maxn],n;

int Find(int x)
{
    if(x!=f[x]) f[x]=Find(f[x]);
    return f[x];
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    int root=-1; for(int i=1;i<=n;i++) if(a[i]==i) root=i;
    int num=0; for(int i=1;i<=n;i++) f[i]=i;

    for(int i=1; i<=n; i++)
    {
        if(i==root) continue;
        int fx=Find(i),fy=Find(a[i]);
        if(fx!=fy) f[fx]=fy;
        else
        {
            if(root==-1) a[i]=i, root=i, num++;
            else
            {
                a[i]=root, num++;
                fx=Find(root); fy=Find(i);
                f[fx]=fy;
            }
        }
    }
    printf("%d\n",num);
    for(int i=1; i<=n; i++) printf("%d ",a[i]);
    printf("\n");
    return 0;
}
时间: 2024-08-18 20:48:19

CodeForces 698B Fix a Tree的相关文章

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

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

【并查集】【模拟】Codeforces 699D Fix a Tree

题目链接: http://codeforces.com/problemset/problem/699/D 题目大意: 通过给定当前节点的父亲给你一棵有错的树,可能有多个根和环,输出改成正确的一棵树至少要修改几个节点的父亲和修改后所有点的父亲值 题目思路: [并查集][模拟] 用并查集把成环的归在一起(类似强连通分量),然后统计分量数并修改. 第一个出现的当作根,其余的每一块连通分量都去掉一条边改为连接到根上. 1 // 2 //by coolxxx 3 ////<bits/stdc++.h>

Problem - D - Codeforces Fix a Tree

Problem - D - Codeforces  Fix a Tree 看完第一名的代码,顿然醒悟... 我可以把所有单独的点全部当成线,那么只有线和环. 如果全是线的话,直接线的条数-1,便是操作数. 如果有环和线,环被打开的同时,接入到线上.那就是线和环的总数-1. 如果只有环的话,把所有的环打开,互相接入,共需n次操作. #include <cstdio> #include <algorithm> using namespace std; const int maxn =

Codeforces 461B Appleman and Tree(木dp)

题目链接:Codeforces 461B Appleman and Tree 题目大意:一棵树,以0节点为根节点,给定每一个节点的父亲节点,以及每一个点的颜色(0表示白色,1表示黑色),切断这棵树的k条边,使得树变成k+1个联通分量.保证每一个联通分量有且仅有1个黑色节点.问有多少种切割方法. 解题思路:树形dp,dp[i][0]和dp[i][1]分别表示子树一下的切割方法中,i节点所在联通块不存在黑节点和已经存在一个黑节点的方案数. #include <cstdio> #include &l

codeforces 161D - Distance in Tree(树形dp)

题目大意: 求出树上距离为k的点对有多少个. 思路分析: dp[i][j] 表示 i 的子树中和 i 的距离为 j 的点数有多少个.注意dp[i] [0] 永远是1的. 然后在处理完一颗子树后,就把自身的dp 更新. 更新之前更新答案. 如果这颗子树到 i 有 x 个距离为j的.那么答案就要加上 dp[i] [ k-j-1] * x; #include <iostream> #include <cstdio> #include <cstring> #include &l

Codeforces 461B Appleman and Tree(树形dp)

题目链接:Codeforces 461B Appleman and Tree 题目大意:一棵树,以0节点为根节点,给定每个节点的父亲节点,以及每个点的颜色(0表示白色,1表示黑色),切断这棵树的k条边,使得树变成k+1个联通分量,保证每个联通分量有且仅有1个黑色节点.问有多少种分割方法. 解题思路:树形dp,dp[i][0]和dp[i][1]分别表示子树一下的分割方法中,i节点所在联通块不存在黑节点和已经存在一个黑节点的方案数. #include <cstdio> #include <c

Educational Codeforces Round 25 G. Tree Queries

题目链接:Educational Codeforces Round 25 G. Tree Queries 题意: 给你一棵树,一开始所有的点全是黑色,有两种操作. 1 x 将x这个点变为黑色,保证第一个操作是这个. 2 x 询问x到任意黑色的点的简单路径上的最小节点编号. 题解: 首先将一个变为黑色的点当成树根,然后dfs一下,预处理出所有点的答案. 然后开一个变量记录一下当前变黑的点的答案cur=min(cur,dp[x]). 每次询问的时候答案就是min(cur,dp[x]). 如果觉得很神

Codeforces.280C.Game on Tree(期望)

题目链接 参考:浅谈期望的线性性(可加性) Codeforces 280C Game on Tree 概率dp 树上随机删子树 求删完次数的期望(这个的前半部分分析并没有看..) \(Description\) 给你一棵有\(n\)个白点的有根树,每次随机选择一个点,将它和它的子树中所有点染黑. 问期望操作多少次后所有点都被染黑? \(Solution\) 期望好玄啊..(好吧是我太弱) 因为概率具有可加性,一棵树可以分解为多棵子树,而子树分解的最终状态就是点,所以我们可以计算每个点的期望操作次

【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