• D. Cyclic Operations Codeforces Round 897 (Div. 2)


    Problem - D - Codeforces

    题目大意:有一个长度为n的数组a,每次操作可以选取一个长度为k的所有数互不相同的数组b,令a[bi]=b[i%k+1],问能否将一个全为零的数组通过任意次操作得到a

    1<=k<=n<=1e5

    思路:通过上述操作,我们发现k中每两个数可以把一个位置修改成目标值,例如对于题目中给的样例1,我们要将a[1]修改成2,那么k就应该等于[1,2,?],要将a[2]修改成3,k就等于[2,3,?],但问号里的数不能与前面的重复也不能超过n,所以可能会覆盖掉之前修改过的数,如果我们从i向a[i]建边,那么存在覆盖就代表着图中存在环。样例1的环如下:

    对于单一约束的点,也就是环以外的点,直接按顺序操作即可,对于环内的点,当环的大小大于k时,也就是如下图:

    我们先按顺序操作[1,2,3],[3,5,4],使位置1,2,3,5上的数都修改完毕,要让位置4上的数也修改好,只能是[4,2,3],这是会把3修改成4,而我们要的是5,要把3改回去,就要操作[3,5,4],第4个位置又不行了,所以这样的环永远会出现错误覆盖的情况。

    但环的大小小于k时,例如1,2互向指向自己,那k=3时的操作就只能是[1,2,1],后面填别的数都会错误的覆盖掉其他位置,而这样又与b种数字不能重复矛盾,所以这样的话也没法修改好。

    综上,只有所有环的大小等于k时才有合法操作方案,我们首先从入度为0的点开始遍历,先用类似拓扑排序的方法去掉所有环外的点,然后再遍历所有环,检查大小是否为k。

    除此之外,还要特判k=1的情况,因为这时只能将第i个位置上的数修改成i,检查a数组即可

    1. #include
    2. //#include<__msvc_all_public_headers.hpp>
    3. using namespace std;
    4. typedef long long ll;
    5. const int N = 1e5 + 5;
    6. int n;
    7. bool vis[N];
    8. int to[N];
    9. int in[N];
    10. void init()
    11. {
    12. for (int i = 1; i <= n; i++)
    13. {
    14. in[i] = 0;
    15. vis[i] = 0;
    16. }
    17. }
    18. int cnt = 0;
    19. void dfs(int u)
    20. {//遍历环
    21. vis[u] = 1;
    22. cnt++;//统计环的大小
    23. int v = to[u];
    24. if (!vis[v])
    25. {
    26. dfs(v);
    27. }
    28. }
    29. void solve()
    30. {
    31. int k;
    32. cin >> n >> k;
    33. init();
    34. if (k == 1)
    35. {//特判k=1的情况
    36. bool flag = 1;
    37. for (int i = 1; i <= n; i++)
    38. {
    39. int x;
    40. cin >> x;
    41. if (x != i)
    42. flag = 0;
    43. }
    44. cout << (flag ? "Yes" : "No") << endl;
    45. return;
    46. }
    47. for (int i = 1; i <= n; i++)
    48. {
    49. int v;
    50. cin >> v;
    51. in[v]++;
    52. to[i] = v;//从i向a[i]建边
    53. }
    54. queue<int>q;
    55. for (int i = 1; i <= n; i++)
    56. {//放入入度为0的点
    57. if (!in[i])
    58. {
    59. q.push(i);
    60. }
    61. }
    62. while (!q.empty())
    63. {//用类似拓扑排序的方式标记所有环外的点
    64. int u = q.front();
    65. q.pop();
    66. vis[u] = 1;
    67. int v = to[u];
    68. in[v]--;
    69. if (!in[v])
    70. {
    71. q.push(v);
    72. }
    73. }
    74. for (int i = 1; i <= n; i++)
    75. {//遍历所有环
    76. if (!vis[i])
    77. {
    78. cnt = 0;
    79. dfs(i);
    80. if (cnt != k)
    81. {//检查环的大小是否等于k
    82. cout << "No" << endl;
    83. return;
    84. }
    85. }
    86. }
    87. cout << "Yes" << endl;
    88. }
    89. int main()
    90. {
    91. ios::sync_with_stdio(false);
    92. cin.tie(0);
    93. cout.tie(0);
    94. int t;
    95. cin >> t;
    96. while (t--)
    97. {
    98. solve();
    99. }
    100. return 0;
    101. }

  • 相关阅读:
    网页字体图标用法
    大文件上传时如何做到 秒传?
    想开发DAYU200,我教你
    shell清理日志,通过mtime筛选文件时间
    USB转双串口CH342与CP2105应用差异
    UE5.1编辑器拓展【一、脚本化资产行为,通知,弹窗,高效复制多个同样的资产】
    解码eBPF可观测性:eBPF如何改变我们所知的观测性
    Lua解释器裁剪
    【Java面试】Redis存在线程安全问题吗?为什么?
    【JavaScript复习九】内置对象Date
  • 原文地址:https://blog.csdn.net/ashbringer233/article/details/132829957