【CF 698B】Fix a Tree(dfs)

【CF 699D】Fix a Tree(dfs)


D. Fix a Tree
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

A tree is an undirected connected graph without cycles.

Let's consider a rooted undirected tree with n vertices, numbered 1 through n. There are many ways to represent such a tree. One way is to create an array with n integers p1, p2, ..., pn, where pi denotes a parent of vertex i (here, for convenience a root is considered its own parent).

For this rooted tree the array p is [2, 3, 3, 2].

Given a sequence p1, p2, ..., pn, one is able to restore a tree:

  1. There must be exactly one index r that pr = r. A vertex r is a root of the tree.
  2. For all other n - 1 vertices i, there is an edge between vertex i and vertex pi.

A sequence p1, p2, ..., pn is called valid if the described procedure generates some (any) rooted tree. For example, for n = 3 sequences(1,2,2)(2,3,1) and (2,1,3) are not valid.

You are given a sequence a1, 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) — the number of vertices in the tree.

The second line contains n integers 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) 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 4(because p4 = 4), which you can see on the left drawing below. One of other correct solutions would be a sequence 2 3 3 2, representing a tree rooted in vertex 3 (right drawing below). On both drawings, roots are painted red.

In the second sample, the given sequence is already valid.


i到0的距离有多远?就是WA到AC……

0写成i过了present也真是。。。TOT


题目大意:n个点的图,每个点有一个前驱,可知这样有n条边。

如果是树的话,除了根指向自己,其他点都会有前驱。


现在给出的是一个任意的图,这样会出现多棵树,或者树中有环,或者两者都存在的情况。

问至少改变几个前驱能把他变成一棵标准的多叉树(除了根指向自己,其他节点都指向非自己的节点)。

并和输入格式一样,输出1~n的前驱。


由于每个点只有一个前驱。

那么可以想到,每棵树(内部有环的暂且也称为树)有且只会有一个环(要么是根指向自己,要么是成环了,但有且只有一个)。


这样通过搜索,可以搜到每一棵“坏树“上的环,改变的其实也就是环上的点(任意一个即可)

这样把所有环拆开,接到一个根上即可。


如果全是非自环的话,这样是保证了最少改变数量。

如果有一个以上自环的话,其实就可以把它当做最终的树根了,这样可以节省改变次数。


最后……一定要看准下标。。。


代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <map>
#include <stack>
#include <algorithm>
#include <cmath>
#define LL long long
#define Pr pair<int,int>
#define VI vector<int>

using namespace std;
const int INF = 0x3f3f3f3f;
const double eps = 1e-8;
const int msz = 1e5+1;

struct Edge
{
    int v,next;
};

Edge eg[666666];
bool vis[233333];
int tp;
int head[233333];
int fth[233333];

void Add(int u,int v)
{
    eg[tp].v = v;
    eg[tp].next = head[u];
    head[u] = tp++;
}

int dfs(int u,int pre)
{
    vis[u] = 1;
    int ans = -1;
    int tmp;
    for(int i = head[u]; i != -1; i = eg[i].next)
    {
        if(eg[i].v == pre || eg[i].v == u) continue;
        //printf("%d %d\n",u,eg[i].v);
        if(fth[u] == eg[i].v && fth[eg[i].v] == fth[u]) ans = u;
        if(vis[eg[i].v])
        {
            if(fth[eg[i].v] == u) ans = eg[i].v;
            else ans = u;
        }
        else
        {
            tmp = dfs(eg[i].v,u);
            if(tmp != -1) ans = tmp;
        }
    }

    if(fth[u] == u) ans = u;
    return ans;
}

int main()
{
    int n;

    scanf("%d",&n);

    memset(head,-1,sizeof(head));
    tp = 0;
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d",&fth[i]);
        Add(i,fth[i]);
        Add(fth[i],i);
    }

    int ans = 0;
    int root = -1;
    int to = -1;
    int tmp[233333];

    memset(vis,0,sizeof(vis));
    for(int i = 1; i <= n; ++i)
    {
        if(vis[i]) continue;
        tmp[ans++] = dfs(i,i);
        if(to == -1 && tmp[ans-1] == fth[tmp[ans-1]])
        {
            ans--;
            to = tmp[ans];
        }
    }

    for(int i = 0; i < ans; ++i)
    {
        if(to == -1) fth[tmp[i]] = tmp[0/*i*/];//i到0的距离。。
        else fth[tmp[i]] = to;
    }

    printf("%d\n",ans);
    for(int i = 1; i <= n; ++i)
    {
        if(i != 1) putchar(' ');
        printf("%d",fth[i]);
    }

    return 0;
}



<think>好的,我现在需要帮助用户了解CF树的相关信息和使用方法。首先,我应该回忆一下CF树的基本概念。CF树是BIRCH聚类算法中的核心数据结构,用于高效处理大规模数据。用户可能已经知道聚类的基本概念,但需要更深入的技术细节。 接下来,我需要确认CF树的组成部分。每个节点由聚类特征(CF)组成,而CF包含三个部分:数据点的数量(N)、线性和(LS)和平方和(SS)。这些统计量帮助快速计算聚类的基本信息,比如质心和半径。需要解释清楚这些术语,并用数学表达式展示,比如LS和SS的公式。 然后,结构方面,CF树是一个平衡树,每个节点有多个子节点,不超过阈值B;叶子节点则不超过阈值L。分裂过程类似于B树,当插入新数据导致节点超过阈值时,会进行分裂。这里可能需要举例说明分裂的过程,比如如何选择最近的子节点,如何合并或分裂节点。 关于参数,用户可能需要知道如何调整B和L来平衡精度和效率。较小的阈值会导致树更深,可能影响效率;较大的阈值可能降低聚类质量。这部分需要结合实际应用场景的建议,比如处理大规模数据时的调参策略。 使用场景方面,CF树适合流式数据或内存有限的情况,因为它可以增量更新,不需要重新计算全部数据。但也要指出其局限性,比如对高维数据效果可能下降,或者球形簇的假设。这能帮助用户判断是否适合他们的需求。 最后,生成相关问题时,需要覆盖结构、参数调优、优缺点、应用场景以及与其他算法的对比。这些问题能引导用户进一步探索CF树的不同方面。同时,确保引用正确的文献,比如用户提供的引用[1]中的聚类特征定义,以及引用[3]中的聚类综述,以增强回答的可信度。 检查是否有遗漏的部分,比如是否解释了CF的三个分量如何计算质心和半径,是否有示例说明插入过程,是否提到了BIRCH算法的步骤。确保数学公式使用正确的LaTeX格式,行内公式用$...$,独立公式用$$...$$。同时,回答结构要清晰,分点说明,帮助用户逐步理解。</think>### CF树(聚类特征树)详解 CF树(Clustering Feature Tree)是层次聚类算法BIRCH的核心数据结构,专为处理大规模数据设计。其核心思想是通过紧凑的聚类特征(CF)逐步构建树形结构,实现高效的数据压缩和聚类分析[^1]。 #### 1. 聚类特征(CF)定义 每个CF包含三个关键统计量: $$ CF = (n, LS, SS) $$ - $n$:子簇中数据点数量 - $LS = \sum_{i=1}^n x_i$(线性和) - $SS = \sum_{i=1}^n x_i^2$(平方和) 通过这些统计量可快速计算: - 质心:$\frac{LS}{n}$ - 簇半径:$\sqrt{\frac{SS}{n} - (\frac{LS}{n})^2}$ #### 2. CF树结构特性 - **平衡树结构**:类似B+树,包含根节点、内部节点和叶节点 - **节点容量限制**: - 内部节点最大分支数:$B$(分支因子) - 叶节点最大CF数:$L$(叶容量) - 簇直径阈值:$T$(控制簇紧密程度) #### 3. 构建过程示例 当插入新数据点$x$时: 1. 从根节点向下寻找最接近的CF路径 2. 到达叶节点后: - 若合并后簇直径$\leq T$:吸收到现有CF - 否则:创建新CF条目 3. 若节点溢出则分裂,分裂策略类似B树 ```python # 伪代码示例 class CFNode: def insert(self, point): if self.is_leaf: candidate = find_closest_cf(point) if merged_diameter(candidate, point) <= T: candidate.update(point) else: create_new_cf(point) if len(CFs) > L: self.split() else: route_to_child(point) ``` #### 4. 关键参数调优 | 参数 | 影响 | 建议值 | |------|------|-------| | $B$ | 树宽度 | 50-200 | | $L$ | 叶节点容量 | 20-100 | | $T$ | 簇紧密度 | 根据数据分布调整 | #### 5. 应用场景 - **流式数据处理**:支持增量更新[^1] - **内存受限环境**:典型内存消耗仅为原始数据的1/10 - **数据预处理**:可生成初步聚类作为其他算法的输入 #### 6. 优缺点分析 ✓ 优点: - 时间复杂度$O(n)$ - 可处理任意形状数据 - 自动处理噪声点 ✗ 局限性: - 对高维数据效果下降 - 依赖数据输入顺序 - 需要合理设置阈值参数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值