[THUPC 2023 初赛] 速战速决 题解
我们将小 I 的初始手牌分为两种情况:手中有两张相同的牌或没有。
容易发现后者等价于小 I 手中的牌为一个
我们猜测小 I 存在一种方法能够使得小 J 只收回一次牌且收回牌数为
题目给出小 J 的出牌顺序
小 I 的出牌方法
解释 :先打
这样我们就做完了初始手牌为排列的情况。
接下来处理小 I 手中存在两张相同的牌的情况,设小 I 初始手中有两张编号为
我们考虑先打其中一张
考虑如何维护,不妨设当前牌堆底部牌为
分情况讨论:
-
另一张
y 在牌堆中,此时如果不及时将牌堆中的y 收回,下一轮小 J 打出的y 就能形成收牌操作,所以此时小 I 打出x 清空牌堆,将牌堆中的y 收在手上,同时保证了下一回合小 J 打出y 后新牌堆顶y 的另一张在小 I 手中。 -
另一张
y 在小 I 手上,这种情况其实可收可不收,但是为了方便,我们同第一种情况打出x 收回整个牌堆,同样保证新牌堆顶y 的另一张在小 I 手中。 -
另一张
y 在小 J 手上,此时小 I 随便打一张牌就可以了,但是注意不能把手中与牌堆顶相同的那张牌打出去。
至此,我们已经做完了这道题,具体在代码中,我们需要维护牌堆和记录每个牌的位置(是否在小 I 手中和是否在牌堆中),为了方便处理可以维护小 I 的手牌。写起来有些细节。
#include<bits/stdc++.h>
#define reg register
#define IL inline
using namespace std;
//#define getchar() (_S==_T&&(_T=(_S=_B)+fread(_B,1,1<<15,stdin),_S==_T)?EOF:*_S++)
char _C,_B[1<<15],*_S=_B,*_T=_B;
IL int read(){
reg int x=0,y=1;
for(_C=getchar();!isdigit(_C);_C=getchar())if(_C=='-')y=-1;
for(;isdigit(_C);_C=getchar())x=(x<<1)+(x<<3)+(_C^48);
return x*y;
}
const int N=3e5+7;
int n,cnt[N],a[N],in[N],s[N],top,Ans[N];
queue<int>q;
IL void Push(reg int x){in[s[++top]=x]=1;}
IL void Pop(reg int x){
while(s[top]!=x)++cnt[s[top]],in[s[top]]=0,q.push(s[top--]);
++cnt[x],in[x]=0,q.push(x),--top;
}
IL int work(){
printf("%d\n%d ",n+2,a[n]);
for(reg int i=1;i<n;++i)printf("%d ",a[i]);
printf("%d %d",a[1],a[1]);
return 0;
}
signed main(){
for(reg int i=n=read();i;--i)cnt[i]=2;
if(n==1)return !puts("-1");
for(reg int i=1;i<=n;++i)--cnt[a[i]=read()];
reg int Start=0;
for(reg int i=n;i&&!Start;--i)if(cnt[i]==2)Start=i;
for(reg int i=n;i;--i)for(reg int j=cnt[i]-(Start==i);j;--j)q.push(i);
if(!Start)return work();
Push(Ans[0]=Start),--cnt[Ans[0]];
for(reg int i=1;i<n;++i){
Push(a[i]);
if(cnt[a[i+1]]||in[a[i+1]]){
Pop(Ans[i]=s[1]);
}
else{
q.front()==s[1]?q.push(s[1]),q.pop():void();
if(in[Ans[i]=q.front()]){
Pop(Ans[i]);
}
else{
Push(Ans[i]),--cnt[Ans[i]];
q.pop();
}
}
}
printf("%d\n",n);
for(reg int i=0;i<n;++i)printf("%d ",Ans[i]);
return 0;
}