放上题目链接:https://ac.nowcoder.com/acm/contest/1099/K
中文题面不做过多解释。
比赛刚结束的时候和队友讨论过这道题,当时说的思路是用双向链表把第一个串的尾和第二个串的头连起来,建立一个数组存状态。每次操作都反转一下第一个串的状态。这个状态数组表示的是下一次是连表头还是表尾。
然而时间太久,下午模拟了一下,成功把自己绕进去。(雾...)
写这篇题解只是想记录一下之前不知道的一个STL list的用法。
对每个链表,同时记录它的正序li和倒序dli。
那么对于一次操作a b,dli b+dli a即为新链表a的正序(前面都是倒序的),li a+ li b为新链表a的倒序。
通过splice()这个函数可以实现链表的拼接。
函数有以下三种声明:
一:void splice ( iterator position, list<T,Allocator>& x );
二:void splice ( iterator position, list<T,Allocator>& x, iterator it );
三:void splice ( iterator position, list<T,Allocator>& x, iterator first, iterator last );
解释:
position 是要操作的list对象的迭代器
list<T Allocator>&x 被剪的对象
对于一:会在position后把list<T Allocator>&x所有的元素到剪接到要操作的list对象
对于二:只会把it的值剪接到要操作的list对象中
对于三:把first 到 last 剪接到要操作的list对象中
(此处引用自:https://blog.csdn.net/Wchenchen0/article/details/83058928)
时间复杂度为O(n),但是此时size()处理时间不为O(n)
(详见:https://blog.csdn.net/russell_tao/article/details/8572000 )
放上此题代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
list <int> li[maxn],dli[maxn];
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
for(int i=1;i<=n;i++)
{
li[i].clear();
li[i].push_back(i);
dli[i].clear();
dli[i].push_back(i);
}
int a,b;
while(m--)
{
scanf("%d%d",&a,&b);
dli[b].splice(dli[b].end(),dli[a]);
li[a].splice(li[a].end(),li[b]);
swap(dli[a],li[a]);
swap(li[a],dli[b]);
li[b].clear();
dli[b].clear();
}
cout<<li[1].size();
for(list <int>::iterator iter=li[1].begin();iter!=li[1].end();iter++)
cout<<" "<<*iter;
cout<<endl;
}
return 0;
}