小军的军训进行到了一半了,今天军训教官搞了一波突然袭击,进行了一个寝的查。
提前了解到查寝消息的小军准备进行一波整理归纳,来使自己的寝室变得更加整洁。具体来说,小军有n件物品,放在n个盒子里,第i个盒子有物品i,小军会进行m次整理,第i次整理,小军会依次在第x个盒子顶拿走物品放入第y个盒子内,直至第x个盒子完全搬空。比如第1个盒子自顶向下有物品1、2,第2个盒子有物品3,将盒子1内的物品搬入盒子2内后结果是: 第1个盒子没有物品,第2个盒子自顶向下是2、1、3
现在,小军告诉你n还有m次操作具体是什么,你能告诉他最后每个盒子内有几个物品,他们具体是什么么?
输入:
一个正整数n代表盒子和物品数,一个正整数m代表整理归纳的次数
接下来m行输入,一行两个正整数x y,代表用上述的方法将盒子x的物品搬到盒子y里
1≤n≤10^5, 1≤m≤10^6, 1≤x,y≤n
题目保证x != y
输出;
有n行输出
第i行,先输出一个正整数k,表示第i个盒子内的物品数,接下来输出n个数,表示第i个盒子自顶向下的物品标号
注意:
行末无空格,文末有回车。
解题思路:
由于复杂度较高,因此我们不能模拟入栈出栈过程;考虑到每一个过程都相当于一个有序的串反向挪到另一个串的后面,因此我们可以用双链表来实现这个过程,而如何简易存储呢?我们可以定义五个数组,前两个存每一个值左右为哪个值,后两个存每一个栈第一个和最后一个元素是哪个值,中间存当前栈有多少个元素
首先进行初始化,每一个栈i开始和结束的值都为i,栈中有1个元素
每一个i左右都为0
之后进行m次移动,设将x移动到y,这里分为三种情况,第一种x元素个数为0,这种不用管,第二种是y元素个数为0,将x的开头记为y的结尾,x的结尾记为y的开头,y元素个数记为x的,x的更改为0.
第三种情况我们先将xy的尾元素连接起来,由于连接后不会再分开,因此并且最多连两次,我们可以将x左右为零的连y,y左右为零的连x,y队尾为x队头,y元素数量加上x的,x元素数量记为零
之后进行n个栈的输出
先将零的输出0
之后若有元素则输出个数,从尾部向前输出,不用管左右,如果刚刚输出过就输出另一边,这样就能保证顺序。
代码:(纯数组模拟hhhh)
#include<iostream>
using namespace std;
long long idx;
int ne[100010][7];
int hd[100010][3];
void init(int n){
for(int i=1;i<=n;i++){
ne[i][0]=i;//存的值
ne[i][1]=0;//左
ne[i][2]=0;//右
hd[i][0]=1;//个数
hd[i][1]=i;//头
hd[i][2]=i;//尾
}
}
int main(){
int n,m;
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin>>n>>m;
init(n);
while(m--){
int x,y;
cin>>x>>y;
if(hd[x][0]==0)continue;
if(hd[y][0]==0){
hd[y][0]+=hd[x][0];
hd[x][0]=0;
hd[y][1]=hd[x][2];
hd[y][2]=hd[x][1];
}
else {
if(ne[hd[y][2]][2]==0)
ne[hd[y][2]][2]=hd[x][2];
else ne[hd[y][2]][1]=hd[x][2];
if(ne[hd[x][2]][1]==0)
ne[hd[x][2]][1]=hd[y][2];
else ne[hd[x][2]][2]=hd[y][2];
hd[y][2]=hd[x][1];
hd[x][2]=hd[y][1];
hd[y][0]+=hd[x][0];
hd[x][0]=0;
}
}
for(int i=1;i<=n;i++){
if(hd[i][0]==0){
cout<<0<<endl;
}
else {
cout<<hd[i][0]<<' ';
int t=hd[i][2];
for(int j=hd[i][2];;){
cout<<j;
if(j!=hd[i][1])cout<<' ';
if(j==hd[i][1]){
cout<<endl;break;
}
if(j==t){
if(ne[j][1]==0){
t=j;
j=ne[j][2];
}
else {
t=j;
j=ne[j][1];
}
}
else{
if(ne[j][1]==t){
t=j;
j=ne[j][2];
}
else {
t=j;
j=ne[j][1];
}
}
}
}
}
}