1867D——Codeforces Round 897 (Div. 2)D题Cyclic Operations

题目

链接

Problem - D - Codeforces

题面

1867D——Codeforces Round 897 (Div. 2)D题Cyclic Operations_第1张图片

翻译

        大致意思就是给你一个长度为n的其中元素值都为0的数组a,和一种操作:构造出一个长度为k的数组l,其中每个元素的值都要在[1, n]以内,然后将每个元素l_{(i\mod k)+1}放到a_{i}中。最后需要我们判断是否可以通过若干次(或者0次)操作将数组a转变成input中给定的数组b。如果可以输出yes否则no

题解

题意解析

        我们可以通过模拟任意操作发现,执行完操作后数组a会多出一个长度为k的环,这里我理解环的方式是将a数组的的值a_{i}作为这个点_{i}指向点。比如:我们模拟操作,构造出一个数组l:\{\left. 1,2,4 \right \}那么数组a将会变成\left \{ 2,4,0,1 \right \}这里的值0可以是任意的值,值的大小取决于执行操作前原有的值。我们会发现执行操作之后,数组a中多了一个环。

        那么如果有多次操作会发生什么情况呢?我们会发现,一定至少会有一个长度为k的环,而这个环之外的值可能会是任意的值,因为原先的操作有可能会被后续的操作所覆盖,因此如果执行完若干次操作之后的数组中存在不是环的地方也是合理的或者说是可以被构造出来的。

        我们考虑完了没有环的情况,接着来考虑有环但长度不是k的情况。有可能嘛?事实上是没有可能的,正如我上文所说,我们操作是覆盖式的,如果要构造出一个比k大的环,那么我们就至少需要两次操作所构成的环是相连的,但再执行第二次操作的时候我们就会破坏第一次操作构造出来的环的结构,因此不可能导致两个环相连构成更大的环。如果要构造出比k小的环,同样需要至少两次操作,但第二次操作一定会破坏第一次构造出来的环,同时第二次操作也会使得环的长度为k,因此比k小的环也不可能。

        因此我们的题目就变成了寻找环,我们的代码思路就是寻找环,并且判断环的长度是否恰好等于k,如果不等于那么一定就错了。

        同时这里我们需要注意k的值是可以为1的,所以我们最好是对k值为1的时候进行一个特判,因为k值为1的时候构造出来的值一定是一个1到n的数组。同时当k值不为1的时候如果第i位出现了i也一定是不合法的,是构造不出来的,因为这样就会出现自环了。我们这里也将这个情况特判掉,这样自环的情况就不会影响我们对判断环和计算环的长度的代码了。

        计算环的方式很好理解,就是一个循环,直到又回到了源点,然后每次移动的时候计数器计数。那么我们如果去寻找环呢?这题的数据量是1e5。如果是纯暴力,每个点遍历过去去判断是否是环,当然是会TLE的,那么我们就可以采用并查集的思路。

        我们可以通过上文的思路去理解输入的数组,首先数组的长度为n,也就是我们有n个点,数组中的每个值都代表这个下标指向值代表的点,因此就有n条有向边,并且每个点会指向一个值,因此我们通过输入画出来的图也一定会有一个环,并且可能会有链指向环,也有可能会有离散的点,但不可能会有离散的链,因此我们可以通过并查集去找环,当涂点的时候发现,下一个点是这次涂的点就是找到环了,然后判断环的长度是否合理,不合理就直接输出NO,合理就继续去寻找环。

完整代码

#include 
#include 
using namespace std;
const int N = 1e5 + 10;
int a[N];
int f[N];
int n, k;

void solve()
{
    cin >> n >> k;
    int flag = 0;
    for (int i = 1; i <= n; i++)
    {
        f[i] = 0;
        cin >> a[i];
        if (a[i] == i && k != 1)
            flag = 1;
        if (k == 1 && a[i] != i)
            flag = 1;
    }
    if (flag == 1)
    {
        cout << "NO" << "\n";
        return;
    }
    else if (k == 1)
    {
        cout << "YES" << "\n";
        return;
    }
    for (int i = 1; i <= n; i++)
    {
        if (f[i] == 0)
        {
            int ind = i;
            while (f[ind] == 0)
            {
                f[ind] = i;
                ind = a[ind];
            }
            if (f[ind] == i)
            {
                int tmp = a[ind], cnt = 1;
                while (tmp != ind)
                {
                    cnt++;
                    tmp = a[tmp];
                }
                if (cnt != k)
                {
                    cout << "NO" << "\n";
                    return;
                }
            }
        }
    }
    cout << "YES" << "\n";
    return;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T;
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

你可能感兴趣的:(算法,c++,图论)