U168834 [NOI2021SDPT3Test2]体育测试

首先考虑全部都是正的怎么做。
直接排个序,排列组合即可。
但此时加入了负数,这就比较难处理。
考虑容斥:
先把所有负数取绝对值,当上标记,然后升序排序(若数大小相同,负的排在前面)。
定义状态 \(f_{i,j}\) 表示前 \(i\) 个数,钦定 \(j\) 个负数不满足的方案数。
为方便转移,定义 \(c_i\) 表示前 \(i\) 个数中有多少个负的。
考虑转移:

  • 若当前为正数,则有 \(f_{i,j} = f_{i-1,j} \cdot (a_i-i-j+c_i+1)\).
  • 若当前为负数,则有 \(f_{i,j} = f_{i-1,j} + f_{i-1,j-1} \cdot(a_i-i-j+c_i)\).

答案显然为 \(\sum_{i=0}^{c_n}(-1)^i \cdot f_{n,i} \cdot (c_n-i)\).

#include<bits/stdc++.h>
#define ll long long

using namespace std;

const int N = 5e3+5,mod = 1e9+7;
 
struct node{
	ll x;
	int op;
	bool operator<(node b)const{
		if(x!=b.x)return x<b.x;
		return op>b.op;
	}
}a[N];
int n;
int c[N];
ll fac[N];
ll f[N][N];
int main() {
	cin>>n;
	
	for(int i=1;i<=n;i++){
		cin>>a[i].x;
		if(a[i].x<0)a[i].op=1,a[i].x=-a[i].x;
	}
	
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++) {
		if(a[i].op)c[i]=1;
		c[i]+=c[i-1];
	}
	
	fac[0]=1;
	for(int i=1;i<=n;i++)
		fac[i]=fac[i-1]*i%mod;
	
	f[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=c[i];j++){
			if(a[i].op==0)f[i][j]=f[i-1][j]*max(0ll,a[i].x-i-j+c[i]+1)%mod;
			else {
				f[i][j]=f[i-1][j];
				if(j)f[i][j]=(f[i][j]+f[i-1][j-1]*max(0ll,a[i].x-i-j+c[i])%mod)%mod;
			}
		}
	}
	
	ll ans=0;
	for(int i=0;i<=c[n];i++){
//		cout<<f[n][i]<<" \n"[i==c[n]];
		if(i&1)ans=(ans-f[n][i]*fac[c[n]-i]%mod+mod)%mod;
		else ans=(ans+f[n][i]*fac[c[n]-i]%mod)%mod;
	}
	
	cout<<ans;
	return 0;
}
posted @ 2025-12-20 08:49  Harvey-zhuhy  阅读(0)  评论(0)    收藏  举报