[LGOJ5151]HKE与他的小朋友——[缩点+vector]

【题目描述】

HKE带着 n n n个小朋友做游戏

现在有 n n n个座位编号为 1 1 1 n n n,这些小朋友也编号 1 1 1 n n n。一开始所有小朋友都坐在相应的座位上。HKE的游戏可用一个 n n n的排列 A ( A 1 , A 2 ⋯ A n ) A(A_1,A_2\cdots A_n) A(A1,A2An) 表示。一轮游戏时,对于所有的 1 ≤ i ≤ n 1\leq i\leq n 1in坐在位置 i i i上的小朋友坐到位置 A i A_i Ai上。

现在游戏进行了 k k k轮,HKE想知道游戏结束后,位置 1 , 2 ⋯ n 1,2\cdots n 1,2n分别坐了几号小朋友?

【输入描述】

第一行 n , k n,k n,k
第二行 A ( A 1 , A 2 ⋯ A n ) A(A_1,A_2\cdots A_n) A(A1,A2An)

【输出描述】
一行 n n n个数表示位置 1 , 2 … n 1,2…n 1,2n上的小朋友的编号。

S a m p l e    I n p u t Sample~~Input Sample  Input

5 5
2 3 1 5 4

S a m p l e    O u t p u t Sample~~Output Sample  Output

2 3 1 5 4

【题意分析】

提供一种暴力但思维清奇的方法:

我们很容易发现,这些小朋友的操作可以用有向边来表示。

很自然地想到这些有向边会连成环,而经过k次操作后小朋友的位置会在环内不断循环。

先用tarjan进行缩点,我们可以用vector把环全部存起来,对于每个小朋友,可以通过k 对环大小取模直接找到最终位置。

时间复杂度 O ( N ) O(N) O(N) t a r j a n O ( n ) tarjanO(n) tarjanO(n)+处理答案 O ( n ) O(n) O(n)

Code :

#include <bits/stdc++.h>
#define rep(x,a,b) for (register int x = a; x <= b; x++)
#define cross(x,a) for (register int x = head[a]; x; x = edge[x].next)
#define MAXN 150000
using namespace std;

struct Front_Link_Star {
    int next, to;
}edge[MAXN];

vector <int> mmp[MAXN];
//vector[x][y]是第x个环的第y个元素

int head[MAXN], dfn[MAXN], low[MAXN], stac[MAXN], DAG[MAXN];
int cnt[MAXN], ans[MAXN], a[MAXN], pos[MAXN];
int _cnt, idx, top, tot_circle, n, k;
bool vis[MAXN];

inline void connect (int u, int v) {
    edge[++_cnt].to = v;
    edge[_cnt].next = head[u];
    head[u] = _cnt;
}

//tarjan
void tarjan (int now) {
    dfn[now] = low[now] = ++idx;
    vis[now] = 1; stac[++top] = now;
    cross (i, now) {
        int v = edge[i].to;
        if (! dfn[v]) {
            tarjan (v);
            low[now] = min (low[now], low[v]);
        }
        else if (vis[v]) low[now] = min (low[now], low[v]);
    }
    if (dfn[now] == low[now]) {
        tot_circle++;
        while (int y = stac[top--]) {
            DAG[y] = tot_circle;
            cnt[DAG[y]]++;
            vis[y] = 0;
            if (now == y) break;
        }
    }
}

int main () {
    scanf ("%d %d", &n, &k);
    rep (i, 1, n) {
        scanf ("%d", &a[i]); connect (i, a[i]);
    }
    rep (i, 1, n) if (! dfn[i]) tarjan (i);
    memset (vis, 0, sizeof (vis));
    rep (i, 1, n) {
        int now = i;
        while (! vis[now]) {
            mmp[DAG[i]].push_back (now);
            vis[now] = 1; now = a[now];
        }
    }
    //用vector把环存起来
    //mmp[i][j]表示第i个环第j个元素
    rep (i, 1, tot_circle) {
        int rest = k % cnt[i]; 
        rep (j, 0, mmp[i].size ()-1) {
            ans[mmp[i][(j+rest) % cnt[i]]] = mmp[i][j];
        }
    }
    //k对环大小取模直接找到最终位置
    rep (i, 1, n) printf ("%d ", ans[i]);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值