「MCOI-05」追杀

HEIMOFA / 2023-08-16 / 原文

「MCOI-05」追杀 洛谷

题目描述

Dream SMP 具有 \(m\) 位玩家,编号为 \(1\)\(m\)。初始时,每一位玩家生命数量为 \(3\)。一位玩家 公认活着(canonically alive) 当且仅当生命值非零。

Dream SMP 经常发生大型战争,于是会有玩家杀(PvP)别的玩家。对于活着玩家 \(u\)\(v\),如果 \(u\)\(v\)\(v\) 的生命数量扣除一次。注意,如果 \(u\)\(v\) 不为公认活着,杀没有影响。

总共按时序记录了 \(n\) 次追杀 \(1,2,\dots,n\),其中第 \(k\) 次追杀为 \(u_k\)\(v_k\)

DreamXD(玩家 \(0\))解锁了时空穿越超能力。他现在可以选取任何 \(i,v\) 使得 \(1\le i\le n+1\) 并且 \(1\le v\le m\),穿越到第 \(i-1\) 次追杀之后与第 \(i\) 次追杀之前,并追杀玩家 \(v\)\(i=n+1\) 表示穿越到第 \(n\) 次追杀后。

不同 \(i\)\(v\) 可能导致最终活着玩家集合不同。对于每一个 \(x\) 使得 \(0\le x\le m\),DreamXD 想知道,有多少种 \(i,v\) 使最后公认活着玩家集合恰好含有 \(x\) 位玩家?


输入格式

第一行两个正整数 \(n,m\)
接下来 \(n\) 行,第 \(k\) 行两个正整数 \(u_k,v_k\),表示在第 \(k\) 时刻,\(u_k\) 追杀 \(v_k\)


输出格式

输出一行 \(m+1\) 个正整数,其中第 \(x\) 个为最终有 \(x-1\) 位玩家活(不包含 Dream)的方案数。


样例输入

2 2
1 2
1 2

样例输出

0 3 3

提示

对于 \(100\%\) 的数据,\(1\le n\le6\times10^4\)\(1\le m\le10^3\)\(1\le u_i,v_i\le m\)


题目描述有点复杂,大意为一个追杀游戏,共有 \(n\) 轮,对于每一轮,会出现 \(u_i\)\(v_i\) 的事件,每个人共有三条命,若有一次事件其中一个人没血了,那么相当于没有发生,现在我们可以穿越到 \([1,n]\) 轮前杀一个人,输出为让最后让 \(x(x\in [0,m])\) 个人活下来的方案数。

很容易想到暴力,枚举每一轮,在每一轮前枚举每一个要杀的人,再跑完所有轮,统计。

但是时间复杂度为 \(O(n^2m)\) 是不可以接受的。

考虑在第 \(i\) 轮前追杀 \(j\),如果 \(j\) 既不是这一轮的追杀者也不是被追杀者,那么这一轮追杀 \(j\) 其实和上一轮追杀 \(j\) 是一样的。

如果 \(j\) 是被追杀者,和上面的情况是一样的。

所以说只需要考虑 \(j\) 是这一轮的追杀者的情况(因为如果你在前面追杀他,说不定他在这一轮就死了,所以需要单独考虑)。

其他的情况直接沿用就行。

最后再把第 \(1\) 轮前的情况单独统计一遍即可。

\(Code:\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
const int N=6e4+5,M=1e3+5;
int u[N],v[N],hp[M],kill[M],tmp[M],ans[M];
//kill[i]=在某一轮追杀i可以让多少人活下来

signed main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld%lld",&u[i],&v[i]);
	for(int i=1;i<=m;i++) hp[i]=3;
	for(int i=1;i<=n;i++){
		if(hp[u[i]]&&hp[v[i]]){
			hp[v[i]]--;
			for(int j=1;j<=m;j++) tmp[j]=hp[j];
			if(tmp[u[i]]==1){
				tmp[u[i]]--;
				for(int j=i+1;j<=n;j++) if(tmp[u[j]]&&tmp[v[j]]) tmp[v[j]]--;
				kill[u[i]]=0;
				for(int j=1;j<=m;j++) if(tmp[j]) kill[u[i]]++;
			}
		}
		for(int j=1;j<=m;j++) ans[kill[j]]++;
	}
	for(int i=1;i<=m;i++){
		for(int j=1;j<=m;j++) hp[j]=3;
		hp[i]--;kill[i]=0;
		for(int j=1;j<=n;j++) if(hp[u[j]]&&hp[v[j]]) hp[v[j]]--;
		for(int j=1;j<=m;j++) if(hp[j]) kill[i]++;
	}
	for(int i=1;i<=m;i++) ans[kill[i]]++;
	for(int i=0;i<=m;i++) printf("%lld ",ans[i]);
	return 0;
}