P7981-Solution
ComplexPlanck · · 题解
题面非常简洁,注意到一次操作之后,
画出这样一张图之后,你会发现这张图有
(样例 2 的图,
并且显然的,这是一个内向树森林,所有点的入度都为
注意到这样一个事实:
第一次操作后,
第二次操作后,
第
(不知道看不看得清)总之就是有
我们不妨对基环树的环和附属子树(设
先讨论附属子树,由于附属子树树高不超过
所以我们可以先跳
那么我们现在只需要处理环以及
由于我们之前已经跳过
注意这里不能每个点都跳这么多次,因为这显然对于每个点,最坏是
显然在递推的过程中,我们可以顺带处理出
至此,这道题的流程就结束了,瓶颈在于那个快速幂,总体上时间复杂度是
#include <bits/stdc++.h>
namespace io
// 省略快读快写
using namespace io;
namespace problems
{
const int N = 500010;
int n, k;
int a[N], b[N], ans[N], pre[N];
bool vis[N];
int idc[N], cidx = 0, large;
void go(void)
{
for (int i = 1; i <= n; ++ i)
b[i] = a[a[i]];
for (int i = 1; i <= n; ++ i)
a[i] = b[i];
return;
}
int ksm(int a, int b, int p)
{
int res = 1;
while (b)
{
if (b & 1) res = 1ll * res * a % p;
a = 1ll * a * a % p, b >>= 1;
}
return res;
}
int main(void)
{
memset(vis, false, sizeof(vis));
read(n), read(k);
for (int i = 1; i <= n; ++ i)
read(a[i]);
int lgn = log2(n) + 1;
while (lgn && k)
{
-- lgn, -- k;
go();
}
if (!k)
{
for (int i = 1; i <= n; ++ i)
writesp(a[i]);
puts(""); return 0;
}
for (int i = 1; i <= n; ++ i)
if (!vis[i])
{
int now = i;
for ( ; !vis[now]; now = a[now])
vis[now] = true;
// 找环
if (idc[now]) continue;
// i 是附属子树上的点,找到了旧环
++ cidx, large = 0;
int st = now;
for ( ; !idc[now]; now = a[now])
idc[now] = cidx, pre[a[now]] = now, ++ large;
// 给环编号
int times = ksm(2, k, large);
// 需要偏移 2^k 次,模数为环长 large
now = st;
while (times --)
now = a[now];
ans[st] = now;
// 找到起点对应的位置
int x = st, y = now;
x = a[x], y = a[y];
while (x != st)
{
ans[x] = y;
x = a[x], y = a[y];
}
// 将环上的点的答案处理掉
}
for (int i = 1; i <= n; ++ i)
if (!idc[i])
// 处理附属子树
ans[i] = pre[ans[a[i]]];
for (int i = 1; i <= n; ++ i)
writesp(ans[i]);
puts("");
return 0;
}
}
int main(void)
{
problems::main();
return 0;
}