【洛谷P8866】喵了个喵
题目
题目链接:https://www.luogu.com.cn/problem/P8866
小 E 喜欢上了一款叫做《喵了个喵》的游戏。这个游戏有一个牌堆和 \(n\) 个可以从栈底删除元素的栈,任务是要通过游戏规则将所有的卡牌消去。开始时牌堆中有 \(m\) 张卡牌,从上到下的图案分别是 \(a_1, a_2,\dots, a_m\)。所有的卡牌一共有 \(k\) 种图案,从 \(1\) 到 \(k\) 编号。牌堆中每一种图案的卡牌都有偶数张。开始时所有的栈都是空的。这个游戏有两种操作:
- 选择一个栈,将牌堆顶上的卡牌放入栈的顶部。如果这么操作后,这个栈最上方的两张牌有相同的图案,则会自动将这两张牌消去。
- 选择两个不同的栈,如果这两个栈栈底的卡牌有相同的图案,则可以将这两张牌消去,原来在栈底上方的卡牌会成为新的栈底。如果不同,则什么也不会做。
这个游戏一共有 \(T\) 关,小 E 一直无法通关。请你帮小 E 设计一下游戏方案,即对于游戏的每一关,给出相应的操作序列使得小 E 可以把所有的卡牌消去。
\(n\leq 300,\sum m\leq 2\times 10^6,k\in\{2n-2,2n-1\}\)。
思路
首先 \(k=2n-2\) 的很好做,每一个元素与其前\(/\)后一个相同元素匹配,把至多两个元素放在一个栈中,消除的时候在栈顶就直接放到栈里,在栈底就放在最后一个空栈里。
\(k=2n-1\) 的时候,可能出现 \(n-1\) 个栈都放了两个元素,但是下一个元素不存在于任何栈中的情况。无论是将这个元素放在最后一个栈,或是任意一个已经放了两个元素的栈中,都可能导致无法消除。
假设一个有两个元素的栈,栈底为 \(x\),栈顶为 \(y\),观察到,如果将这最后一个元素 \(z\) 放在这个栈上面,若在牌堆中下一个 \(x\) 出现的比下一个 \(y\) 早,那么其实不会有任何的影响。因为利用最后一个空栈将 \(x\) 消除后,又会变为原来的情况。
如果所有栈都不满足下一个 \(x\) 出现的比下一个 \(y\) 早,那么只需要将 \(z\) 放在那个空栈中,然后把 \(n\) 个 \(x\) 中在牌堆中最早出现的那一个所在的栈设为新的空栈即可。因为这一个 \(x\) 一定可以通过操作 \(1\) 来消除。
需要注意的是,当一个栈被定义为“空栈”的时候,无论那个栈是否还存在元素,都不可以往里面再添加元素,直到下一次特殊情况出现。否则可能导致因为加入新的元素,这个栈底无法通过操作 \(1\) 来消除。
实现的话我比较无脑,直接维护 \(4\) 个 \(\rm set\),分别储存 有 \(0\) 个元素(注意不等价于上文的“空栈”,那个“空栈”是用来处理特殊情况的空栈)\(\ /\ 1\) 个元素\(\ /\ 2\) 个元素且 \(x\) 先于 \(y\ /\ 2\) 个元素且 \(x\) 后于 \(y\) 的栈的编号。注意“空栈”需要特殊记下来。
时间复杂度 \(O(m\log n)\)。
代码
#include <bits/stdc++.h>
#define mp make_pair
#define fi first
#define se second
using namespace std;
const int N=610,M=2000010;
int Q,n,m,k,pos,a[M],bel[N],st[N][4];
queue<pair<int,int> > q;
set<int> s0,s1,s2,s3;
vector<int> nxt[N];
int main()
{
scanf("%d",&Q);
while (Q--)
{
s0.clear(); s1.clear(); s2.clear(); s3.clear();
scanf("%d%d%d",&n,&m,&k);
for (int i=1;i<=m;i++) scanf("%d",&a[i]);
for (int i=m;i>=1;i--) nxt[a[i]].push_back(i);
for (int i=2;i<=n;i++) s0.insert(i);
pos=1;
for (int i=1;i<=m;i++)
{
nxt[a[i]].pop_back();
int p=bel[a[i]];
if (!p)
{
if (s0.size())
{
int id=*s0.begin();
q.push(mp(id,-1));
s1.insert(id); s0.erase(s0.begin());
st[id][1]=a[i]; bel[a[i]]=id;
}
else if (s1.size())
{
int id=*s1.begin();
if (id==pos) id=*(++s1.begin());
q.push(mp(id,-1));
s1.erase(s1.find(id));
if (nxt[st[id][1]].back()>nxt[a[i]].back()) s2.insert(id);
if (nxt[st[id][1]].back()<nxt[a[i]].back()) s3.insert(id);
st[id][2]=a[i]; bel[a[i]]=id;
}
else if (s3.size())
{
int id=*s3.begin();
q.push(mp(id,-1));
st[id][3]=a[i]; bel[a[i]]=id;
}
else
{
q.push(mp(pos,-1));
s1.insert(pos);
st[pos][1]=a[i]; bel[a[i]]=pos;
for (int j=i+1;j<=m;j++)
if (st[bel[a[j]]][1]==a[j]) { pos=bel[a[j]]; break; }
}
}
else if (st[p][1]==a[i] && !st[p][2])
{
q.push(mp(p,-1));
s1.erase(s1.find(p));
st[p][1]=0; bel[a[i]]=0;
if (p!=pos) s0.insert(p);
}
else if (st[p][2]==a[i])
{
q.push(mp(p,-1));
s1.insert(p); s2.erase(s2.find(p));
st[p][2]=0; bel[a[i]]=0;
}
else if (st[p][3]==a[i])
{
q.push(mp(p,-1));
st[p][3]=0; bel[a[i]]=0;
}
else if (st[p][1]==a[i] && st[p][2])
{
q.push(mp(pos,-1)); q.push(mp(pos,p));
if (!st[p][3])
{
s1.insert(p); s3.erase(s3.find(p));
st[p][1]=st[p][2]; st[p][2]=0;
}
else
{
if (nxt[st[p][2]].back()>nxt[st[p][3]].back())
s2.insert(p),s3.erase(s3.find(p));
st[p][1]=st[p][2]; st[p][2]=st[p][3]; st[p][3]=0;
}
bel[a[i]]=0;
}
}
printf("%d\n",(int)q.size());
for (;q.size();q.pop())
{
pair<int,int> res=q.front();
if (res.se==-1) printf("1 %d\n",res.fi);
if (res.se!=-1) printf("2 %d %d\n",res.fi,res.se);
}
}
return 0;
}