传送门:P5151
不难发现小朋友的运动是一种只和座位有关的有方向的运动,因此可以用点来存储座位,用有向边来存储小朋友的运动轨迹。
注意到点数等于边数,因此这张图只能是一个或多个环,才能满足所有人均能多次移动的性质。则问题可以简化为:给定一些环,求在环上运动 k 次后各点的位置。
那么可以很简单的想到判环。在这里直接使用 bfs 判环即可。在判环的同时,还要记录下每个环的大小 Ti ,用于简化每个小朋友的位移量 xi = k mod Ti。并将每个环断开扔入一个链表里面,辅助后续查询。代码如下。
vector<int> list[MAXN];//存储环的链表,这里直接用vector代替
int land[MAXN][2];
//存储与环有关的数据
//第0维存储环的编号,第1位存储这该元素是环的第几号元素
int nxt[MAXN];//存下一个点的编号
int T[MAXN];//记录环的大小
int S[MAXN];//手写栈
int lis;//记录环的编号
void bfs1(int st){
int x = st/*遍历环上的每一个节点*/, tops = 0/*栈顶*/,first = 1/*只是个标记*/;
while (x != st || first){//遍历环
first = 0;
T[st]++;//环的大小增加
list[lis].push_back(x);//把点扔到链表里
land[x][0] = lis;
land[x][1] = tops;//记录有关信息
S[tops++] = x;//入栈
x = nxt[x];//遍历下一个点
}
while (tops--){//修改每一个点记录的环大小
T[ S[tops] ] = T[st];
}//这里也可以不用栈存储,但是手写栈是最简单的。
lis++;//环的编号增加
}
判环之后就是处理答案部分,但是要注意的一点是,这道题不是让你输出每个点在 k 次移动后的位置,而是 k 次移动后每个点上是原来的哪个点。很容易写出这样的代码。
int bfs2(int st, int k){
int step = k % T[st];//计算有用的位移量
step += land[st][1];//计算相对于环中一号点的位移量
if (step >= T[st]) step -= T[st];//控制位移量在1 ~ T_i
return list[ land[st][0] ][step];//返回该点坐标
}
for (int i = 1; i <= n; i++) ans[bfs2(i, k)] = i;
//枚举每个点,计算其结束坐标
这些工作都做完好,就可以愉快的切掉这道题了。
完整无修代码
#include <iostream>
#include <vector>
#define MAXN 100005
using namespace std;
vector<int> list[MAXN];
int land[MAXN][2];
int nxt[MAXN],T[MAXN],S[MAXN],ans[MAXN];
int lis;
void bfs1(int st){
int x = st, tops = 0,first = 1;
while (x != st || first){
first = 0;
T[st]++;
list[lis].push_back(x);
land[x][0] = lis;
land[x][1] = tops;
S[tops++] = x;
x = nxt[x];
}
while (tops--){
T[ S[tops] ] = T[st];
}
lis++;
}
int bfs2(int st, int k){
int step = k % T[st];
step += land[st][1];
if (step >= T[st]) step -= T[st];
return list[ land[st][0] ][step];
}
int main(){
int n, k;
cin>>n>>k;
for (int i = 1; i <= n; i++) cin>>nxt[i];
for (int i = 1; i <= n; i++) if (!T[i]) bfs1(i);
for (int i = 1; i <= n; i++) ans[bfs2(i, k)] = i;
for (int i = 1; i <= n; i++) cout<<ans[i]<<" ";
return 0;
}