Loading

【题解】ARC127E - Priority Queue

给定一个长度为 \(N+M\)\(1/2\) 序列,\(1\) 表示向优先队列中加入一个元素,\(2\) 表示删除最大值,恰有 \(N\)\(1\)\(M\)\(2\),且保证序列合法。问有多少种可能的结束状态,依次加入的必须是一个长度为 \(N\) 的排列。

很好的思维题,观察一下不难发现如果对于操作序列可以拆成若干段,每一段由先加入 \(a\) 个元素,再删除 \(b\) 个元素 \((a\ge b)\)​ 组成,那么这一段要保留 \(a-b\) 个元素。那么对于每个加入的元素只用考虑前面保留的元素 \(>\) 前面最少保留的元素即可。

所以我们简单用栈即可求出划分。为什么要这样划分,因为这样划分后,我们每次加入的元素递增即可保证不会漏掉方案。

定义状态 \(f_{i,j}\) 表示最后一个数是 \(i\),选出了 \(j\) 个数,保留了 \(i-j\) 个数,只要满足 \(j+pre_j\le i\) 即可转移 \(f_{i,j} = \sum\limits_{0\le k<i} f_{k, j - 1}\),前缀和一下即可。时间复杂度 \(\mathcal{O}(N^2)\)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 5005
#define P 998244353
using namespace std;
int n, m, u[N], v[N], top, a[N], f[N][N], s[N][N];
int main(){
	scanf("%d%d", &n, &m);
	rep(i, 1, n + m){
		int x;scanf("%d", &x);
		if(1 == x)u[++top] = 1, v[top] = 0;
		else{
			v[top]++;
			while(u[top] < v[top])u[top - 1] += u[top], v[top - 1] += v[top], top--;
		}
	}
	m = 0; int sz = 0;
	rep(i, 1, top){
	//cout << "ss " << u[i] << " " << v[i] << endl;
		sz += u[i] - v[i];
		rep(j, 1, v[i])
			a[++m] = sz;
	}
	f[0][0] = s[0][0] = 1;
	//rep(i, 1, m)printf("%d ", a[i]);putchar('\n');
	rep(i, 1, n){
		rep(j, 1, i){
			if(a[j] <= i - j)f[i][j] = s[i - 1][j - 1];
			s[i][j] = (s[i - 1][j] + f[i][j]) % P;
		}s[i][0] = 1;
	}
	int sum = 0;
	rep(i, 0, n)sum += f[i][m], sum %= P;
	cout << sum << endl;
	return 0;
}
/*
10 1
1 2 1 1 1 1 1 1 1 1 1
*/
posted @ 2021-10-16 19:40  7KByte  阅读(281)  评论(0编辑  收藏  举报