跳到主要内容
原文196 词数1 分钟

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

概述

同一点数作为起始牌时,处理到它的末次出现位置,桌面均仅剩该点数,因此最终状态相同。将每种点数连向其末次出现位置之后的点数,得到以哨兵为根的树。每局最终保留的牌值对应起点到根路径上的非根结点。以各点数的出现次数作为初始权值,按末次出现位置递增向父亲累加,即可用三个数组求出所有答案,时间复杂度为 O(n+m)。

解题思路​

设 px,cxp_x,c_x 分别为点数 xx 的末次出现位置和出现次数,约定 an+1=0,p0=n+1a_{n+1}=0,p_0=n+1。

任意以 xx 开局,处理至 pxp_x 时桌面均仅剩 xx,故最终序列为 xx 接上后缀 [px+1,n][p_x+1,n] 的最终序列。连边:

x→apx+1x\to a_{p_x+1}

沿边 pp 严格递增,形成以 00 为根的树。每局最终牌值恰为 aia_i 到根路径上的非 00 结点,故答案是以 cxc_x 为权的子树和。按 pxp_x 递增向父亲累加即可,时间复杂度为 O(n+m)O(n+m)。

参考代码​

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

const int N=500005;
int a[N],last[N],ans[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];
last[a[i]]=i;
ans[a[i]]++;
}
for(int i=1;i<=n;i++)if(last[a[i]]==i)ans[a[i+1]]+=ans[a[i]];
for(int i=1;i<=m;i++)cout<<ans[i]<<' ';
return 0;
}