题解:[ARC148E] ≥ K

好题。

考虑我们将序列中本质相同的元素缩成一个,然后考虑计数。

我们将序列中的数分为两类,分别是 \(a_i < \frac{k}{2}\)\(a_i \ge\frac{k}{2}\),之所以这么分是因为第一类数不能相邻,第二类数可以相邻,第一类数和第二类数之间有相邻的条件。

从大到小考虑第二类数,比如当前枚举到了 \(a_i\),把 \(<k-a_i\) 的第一类数加入序列中,因为这些第一类数和 \(a_i\) 不能相邻,并维护当前序列上的空位,一直做直到所有数都填到了序列里面。

记空位个数为 \(sum\),答案为 \(ans\),考虑加入一个数对答案和空位的贡献。

  • 第一类数,它只能单独填到空位里面,并且会占据空位:

\[sum \leftarrow sum-cnt_{a_i},ans=ans\cdot \binom{sum}{cnt_{a_i}} \]

  • 第二类数,它可以填到任意空位,并且会增加空位:

\[sum \leftarrow sum+cnt_{a_i},ans=ans\cdot \binom{sum+cnt_{a_i}-1}{cnt_{a_i}-1} \]

然后做完了,实现非常丑陋,写挂了和题解拍才拍出来。

复杂度 \(O(n \log n)\)

代码实现

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
int binpow(int a,int b){
	if(!b) return 1;
	int res=binpow(a,b/2);
	if(b&1) return res*res%mod*a%mod;
	else return res*res%mod;
}
vector<int> v;
int n,k,a[200005],ans,sum,now,fac[200005];
map<int,int> cnt;
int C(int n,int m){
	if(n<m) return 0;
	if(n<0 || m<0){
		if(n==m) return 1;
		else return 0;
	}
	return fac[n]*binpow(fac[m],mod-2)%mod*binpow(fac[n-m],mod-2)%mod;
}
bool cmp(int x,int y){
	return a[x]<a[y];
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>k;
	fac[0]=1;
	for(int i=1;i<=n;i++){
		fac[i]=fac[i-1]*i%mod;
	}
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(!cnt[a[i]]) if(a[i]<(k+1)/2) v.push_back(i);
		cnt[a[i]]++;
	}
	sort(v.begin(),v.end(),cmp);
	ans=1,sum=1,now=0;
	if(!cnt.size()){
		cout<<0;
		return 0;
	}
	for(auto it=prev(cnt.end());;it--){
		if(it->first<(k+1)/2){
			break;
		}
		while(now<v.size() && a[v[now]]+it->first<k){
			ans=ans*C(sum,cnt[a[v[now]]])%mod;
			sum-=cnt[a[v[now]]];
			now++;
		}
		ans=ans*C(sum+it->second-1,sum-1)%mod;
		sum+=it->second;
		if(it==cnt.begin()) break;
	}
	while(now<v.size()){
		ans=ans*C(sum,cnt[a[v[now]]])%mod;
		sum-=cnt[a[v[now]]];
		now++;
	}
	cout<<ans;
	return 0;
}
posted @ 2025-04-03 17:48  _Kenma  阅读(28)  评论(0)    收藏  举报