【计数】【组合数】价值序列

124 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

题目链接:

ac.nowcoder.com/acm/contest…

在这里插入图片描述


本题不要计算序列a的价值,因为从这个方面考虑很难做出来。

应该从取序列中的哪些值才能使序列a的价值不变来考虑。

对于三个数来讨论:

  • ai<aj<aka_i < a_j <a_kai>aj>aka_i >a_j>a_k

删除中间的数,序列的价值不会变化。如果中间相同的数有k个,情况数为2k2^k

  • ai<aj>aka_i < a_j >a_kai>aj<aka_i >a_j<a_k

删除中间的值,序列价值会发生变化,所以不能删除。但是如果中间的值有多个相等的话,要至少留一个值即可。如果相等数的个数为k,则情况数为2k12^k-1(去除为空的情况)

接下来,就是对中间的相同的数进行讨论了

把相等的数看成一个连通块,设为[i,j][i,j]

  • 如果满足ai1<ai=...=aj<aj+1a_{i-1}<a_i=...=a_j<a_{j+1}ai1>ai=...=aj>aj+1a_{i-1}>a_i=...=a_j>a_{j+1}(且要满足i>1,j<ni>1,j<n),则区间[i,j][i,j]的数可以全部删除,不影响价值,这样的区间对答案贡献有2ji+12^{j-i+1}种方案。

  • 否则区间[i,j][i,j]的数必须保留一个,这样的区间对答案贡献2ji+112^{j-i+1}-1 种方案。把所有区间的贡献连乘起来就是答案。


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

using ll = long long;
const int N = 1e5 + 5, mod = 998244353;

vector<ll> fac(N);
void solve()
{
	int n;
	cin >> n;
	vector<int> a(n);
	for(auto &i : a) cin >> i;
	
	ll res = 1;
	for(int i = 0, j = 0; i < n; i++)	
	{
		j = i;
		while(j < n - 1 && a[j + 1] == a[i]) j++;
		
		if(i - 1 >= 0 and j + 1 < n and (a[i - 1] < a[i] && a[i] < a[j + 1] || a[i - 1] > a[i] && a[i] > a[j + 1]))
			res = res * fac[j - i + 1] % mod;
		else res = res * (fac[j - i + 1] - 1) % mod;
		i = j;
	}
	cout << res << "\n";
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	fac[0] = 1;
	for(int i = 1; i < N; i++) 
		fac[i] = fac[i - 1] * 2 % mod;
	int t;
	cin >> t;
//	t = 1;
	while(t--) solve();
	return 0;
}