Skip to main content
Original260 words1 min

题解:P17830 [CSP-J 2026] 纸牌游戏

Summary

设每局结束时桌面上的牌值构成集合。若本局牌值在后续出现过,桌面状态复用其右侧最近相同牌对应的集合;否则在后缀集合中加入当前牌值。倒序构造这些集合的链表表示,每局对应一个链首结点。统计各链首的出现次数后,按结点依赖关系传递计数,即可得到每种牌值最终留在桌面的局数,时间复杂度为 O(n+m)。

解题思路​

令 TiT_i 表示第 ii 局最终桌面的点数集合,uiu_i 表示 ii 右侧最近的 aia_i 位置;若不存在,则 ui=0u_i=0,并约定 Tn+1=∅T_{n+1}=\varnothing。若 ui>0u_i>0,处理到 uiu_i 会清空桌面并重新放上 aia_i;否则 aia_i 会保留。因此:

Ti={Tuiui>0Ti+1∪{ ai }ui=0T_i= \begin{cases} T_{u_i} & u_i>0 \\ T_{i+1}\cup\set{a_i} & u_i=0 \end{cases}

倒序构造链表,链首为 rtirt_i:ui>0u_i>0 时复用 rtuirt_{u_i},否则新建 aia_i 结点并接到 rti+1rt_{i+1}。每个 rtirt_i 计数一次;结点按倒序创建,后继编号更小,故按编号递减传递计数。kk 的末次出现结点计数即答案,复杂度为 O(n+m)O(n+m)。

参考代码​

557 Bcpp
#include <bits/stdc++.h>
using namespace std;

const int N=500005;
int a[N],last[N],rt[N],nxt[N],id[N],cnt[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
int tot=0;
for(int i=n;i>=1;i--)
{
if(last[a[i]])rt[i]=rt[last[a[i]]];
else
{
tot++;
id[a[i]]=tot;
nxt[tot]=rt[i+1];
rt[i]=tot;
}
last[a[i]]=i;
}
for(int i=1;i<=n;i++)cnt[rt[i]]++;
for(int i=tot;i>=1;i--)cnt[nxt[i]]+=cnt[i];
cout<<cnt[id[1]];
for(int i=2;i<=m;i++)cout<<' '<<cnt[id[i]];
return 0;
}