「CF1513E Cost Equilibrium」

题目大意

给出一个序列 \(a\),求这个序列存在多少种排列是 balanced。

在开始时对序列中的每个元素规定其为源点还是汇点,接下来的操作中,每次可以选择 \(i,j\),要满足 \(i\) 是源点,\(j\) 是汇点,可以选择一个任意正整数 \(x\),使元素 \(i\) 的权值减 \(x\),元素 \(j\) 的权值加 \(x\),需要花费 \(|i-j|x\) 的代价。

如果对于任意的可以把序列存在至少一种规定源点汇点的方案使得对于把权值全部变为相同的任意的操作方案的代价是相同的,那么就称这个序列为 balanced。

分析

如果平均数不是整数显然答案为 \(0\)

然后把序列分成三部分:

  1. 权值小于平均数的 \(b_1,b_2,\dots b_p\)
  2. 权值等于平均数的 \(\overline{a},\overline{a},\dots\)
  3. 权值大于平均数的 \(c_1,c_2,\dots c_q\)

其中等于平均数的那些数是的权值是不能改变权值的,所以不需要考虑它们是源点还是汇点。

经过简单的考虑可以得到 \(b\) 中只能做汇点,\(c\) 中只能做源点。

\(|b|=1\)\(|c|=1\) 时可以发现不论怎么排都是满足条件的,所以可以直接用多重集的排列数计算。

下面只考虑 \(1<|b|\)\(1<|c|\) 的情况。

如果存在某个源点前后都存在汇点可以发现必然存在方案使得代价不同,所以源点必须全部放在汇点前(或汇点后)。可以枚举最后的一个源点放置的位置 \(o\),那么这个位置的贡献为

\[(\sum_{i=1}^{k}\frac{(o-1)!}{\prod_{j=1}^{k}(s_j-[i=j])!(o-1-\sum_{j=1}^ks_j)!})(\frac{(n-o)!}{\prod_{i=1}^lt_i!(n-o-\sum_{i=1}^lt_i)!}) \]

其中 \(b\) 中存在 \(k\) 种不同的数,第 \(i\) 种数出现了 \(s_i\) 次;\(c\) 中存在 \(l\) 种不同的数,第 \(i\) 种数出现了 \(t_i\) 次。

其中

\[\sum_{i=1}^{k}\frac{1}{\prod_{j=1}^{k}(s_j-[i=j])!} \]

\[\frac{1}{\prod_{i=1}^lt_i!} \]

可以 \(\mathcal{O}(n)\) 预处理,\(\mathcal{O}(1)\) 计算每个 \(o\) 的贡献,把这些贡献加起来即可。

因为这样计算的是汇点在源点前的方案,所以最终答案需要乘二。

综上可以做到 \(\mathcal{O}(n\log n)\) 计算最终结果,复杂度瓶颈仅在于 sort

代码

inline int Pow(int a,int b,const int mod)
{
	a%=mod;
	if(!a)
	{
		return 0;
	}
	register int result(1);
	for(;b;a=1ll*a*a%mod,b>>=1)
	{
		if(b&1)
		{
			result=1ll*result*a%mod;
		}
	}
	return result;
}
#define POW(a,b) Pow(a,b,MOD)
#define INV(x) Pow(x,MOD-2,MOD)
const int MAXN=1e5+5;
const int MOD=1e9+7;
int n;
int arr[MAXN];
int fac[MAXN];
int inv[MAXN];
inline LL C(const int n,const int m)
{
	if(n<m)
	{
		return 0;
	}
	return 1ll*fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
int num1[MAXN];
int num2[MAXN];
int main()
{
	Read(n);
	fac[0]=1;
	REP(i,1,n)
	{
		fac[i]=1ll*fac[i-1]*i%MOD;
	}
	inv[n]=INV(fac[n]);
	DOW(i,n-1,0)
	{
		inv[i]=inv[i+1]*(i+1ll)%MOD;
	}
	LL sum(0);
	REP(i,1,n)
	{
		Read(arr[i]);
		sum+=arr[i];
	}
	if(sum%n)
	{
		Writeln(0);
		return 0;
	}
	int ba=sum/n;
	sort(arr+1,arr+1+n);
	int cnt1(0),cnt2(0),cnt3(0),s(0),s2(0);
	arr[0]=arr[n+1]=-1;
	REP(i,1,n)
	{
		if(ba<=arr[i])
		{
			if(arr[i]==ba)
			{
				cnt3=i;
			}
			break;
		}
		++s;
		if(arr[i]^arr[i-1])
		{
			num1[++cnt1]=1;
		}
		else
		{
			++num1[cnt1];
		}
	}
	DOW(i,n,1)
	{
		if(arr[i]<=ba)
		{
			if(arr[i]==ba)
			{
				cnt3=i-cnt3+1;
			}
			break;
		}
		++s2;
		if(arr[i]^arr[i+1])
		{
			num2[++cnt2]=1;
		}
		else
		{
			++num2[cnt2];
		}
	}
	if(!cnt1)
	{
		Writeln(1);
		return 0;
	}
	if(s==1||s2==1)
	{
		int answer(1ll*fac[n]*inv[cnt3]%MOD);
		REP(i,1,cnt1)
		{
			answer=1ll*answer*inv[num1[i]]%MOD;
		}
		REP(i,1,cnt2)
		{
			answer=1ll*answer*inv[num2[i]]%MOD;
		}
		Writeln(answer);
		return 0;
	}
	int q(0),p(1),p2(1);
	REP(i,1,cnt1)
	{
		p=1ll*p*inv[num1[i]]%MOD;
	}
	REP(i,1,cnt2)
	{
		p2=1ll*p2*inv[num2[i]]%MOD;
	}
	REP(i,1,cnt1)
	{
		q=(q+1ll*p*fac[num1[i]]%MOD*inv[num1[i]-1])%MOD;
	}
	int answer(0);
	REP(i,s,n)
	{
		if(n<i+s2)
		{
			break;
		}
		LL add(1ll*fac[i-1]*inv[i-s]%MOD*q%MOD*fac[n-i]%MOD*inv[n-i-s2]%MOD*p2%MOD);
		answer=(answer+add)%MOD;
	}
	Writeln(answer*2ll%MOD);
	return 0;
}
posted @ 2021-05-20 13:27  SxyLimit  阅读(97)  评论(0编辑  收藏  举报