Divide by Zero 2021 and Codeforces Round #714 (Div. 2) E. Cost Equilibrium

题意

一个序列是好的序列,满足可以通过下面的操作使得这个序列的所有元素都相等。
操作:选择序列中的两个下标i,j,在选择一个x(0 < x <= ai),ai - x,aj + x,这个操作有一个代价为|i - j| * x,且每次操作之后在之后的操作中,不能给ai执行增加x的操作,不能给aj执行减去x的操作。
一个平衡的序列是指把一个序列变为好的序列的最小代价和最大代价相等。

输入

输入一个n表示序列长度,下面一行输入n个数表示这个序列。

输出

可以对这个序列重新排序,询问最后有多少序列个是满足平衡序列(两个序列不相等满足至少有一个下标的值不同)。

思路

首先,对一个数 + x,一个数 - x。所有数的总和是不变的,所有最后生成的好的序列是唯一的,等于∑ai / n。
造成一个序列值不相同情况为在这里插入图片描述
所以为避免这种情况,比平均值大的和比平均值小的应该分别放在两边。和平均值相等的可以放在任意位置,因为他可以起传递作用,并不影响两个值直接的变化。
之后就变成组合数学中的问题:
1.计算比平均值大和比平均值小的排列方案数,也就等价于计算多重集的排列数。 S!/(s1! * s2! *…sk!)S为总个数,si它的一种子元素的个数一共有k种。
2.两个可以一前一后有两种方案(特殊当他两的数目一个值为1时可以任意排列,即不存在图片中的情况)
3.等于平均值的数可以任意排列,可以等价于有n个坑,k个完全相同的箱子可以放在坑里,一个坑内是没有个数限制的,有多少种方案。是一个多重集的排列问题,可以使用隔板法解决,答案为C(n + k - 1,n - 1).

答案为这些之积、

#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10,mod = 1e9 + 7;
map<int ,int > mp;
int a[N];
int fact[N],infact[N];
int qmi(int a,int b){
	int res = 1;
	while(b){
		if(b & 1) res = 1ll * res * a % mod;
		a = 1ll * a * a % mod;
		b >>= 1;
	}
	return res;
}
void init(){
	fact[0] = infact[0] = 1;
	for(int i = 1;i < N;i++){
		fact[i] = 1ll * fact[i - 1] * i % mod;
		infact[i] = 1ll * infact[i - 1] * qmi(i,mod - 2) % mod;
	}
}
int C(int a,int b){
	if(a < b) return 0;
	return 1ll * fact[a] * infact[b]  % mod * infact[a - b] % mod; 
}
int main(){
	init();
	int lans = 1,rans = 1;
	int ans = 1;
	ll sum = 0;
	int n;
	scanf("%d",&n);
	for(int i = 1;i <= n;i++){
		int x;
		scanf("%d",&x);
		mp[x]++;
		sum += x;
	}
	int p = sum / n;
	if(sum % n){
		puts("0");
		return 0;
	}
	int lcnt = 0,rcnt = 0;
	for(auto item:mp){
		int x = item.first;
		int cnt = item.second;
//		cout << x << ' ' << cnt <<endl;
		if(x < p) lans = 1ll * lans * infact[cnt] % mod,lcnt += cnt;
		else if(x > p) rans = 1ll * rans * infact[cnt] % mod,rcnt += cnt;  
	}
	lans = 1ll * lans * fact[lcnt] % mod;
	rans = 1ll * rans * fact[rcnt] % mod;
	int cnt = lcnt + rcnt + 1;
	int k = mp[p];
	ans = 1ll * lans * rans % mod ;
//	cout << ans << endl;
	if(lcnt == 1) ans = 1ll * ans * (rcnt + 1) % mod;
	else if(rcnt == 1) ans = 1ll * ans * (lcnt + 1) % mod;
	else if(lcnt && rcnt)ans = 1ll * ans * 2 % mod;  
//	cout << ans << endl;
	ans = 1ll * C(cnt + k - 1,cnt - 1) * ans % mod;
	cout << ans << endl;
	
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值