codeforces938E Max History 组合数学

题目链接:戳这里

题目大意:

我们定义f(a)为:
1、开始时,f(a)=0,M=1。
2、对于每个2<=i<=n,如果a[M]<a[i],那么f(a)=f(a)+a[M],M=i。
现在对于一个给定的数组a,求其所有排列的f(a)之和,答案对1e9+7取模。

题解:

一开始以为是把f(a)变成a[M],然后不会做。

后来发现是题看错了QAQ。

既然是加上,那么我们可以分开统计每个数的贡献。

如果一个数要有贡献,那么在这个排列中,排在该数前的数都比该数小,排在该数后的都比该数大--->也就是顺序排列。

所以一个数的贡献次数为:

(图从别的博客扒的)

也就是从n个位置中找n-i+1个大于等于该数的位置,其中该数在第一个位置,前i-1个位置和后n-i的位置的数都任意排列的值。

化简一下是n!/(n-i+1)。

那么一个数的贡献就是贡献次数*这个数*出现次数。

加起来即可,除法可以用逆元解决。

代码:

#include<bits/stdc++.h>
#define mod 1000000007
#define maxn 1000005
using namespace std;
typedef long long LL;
int read()
{
	char c;int sum=0,f=1;c=getchar();
	while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
	return sum*f;
}
int n;
int a[maxn];
LL ksm(LL a,LL b)
{
	LL ret=1;
	while(b)
	{
		if(b&1) ret=ret*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ret;
}
LL A=1,ans;
int main()
{
	n=read();
	for(int i=1;i<=n;i++)
	a[i]=read(),A=A*i%mod;
	sort(a+1,a+1+n);
	int pos;
	for(int i=1;i<=n;i=pos)
	{
		pos=i;
		while(a[i]==a[pos] && pos<=n) pos++;  
        if(pos<=n)
		ans=(ans+A*ksm(n-i+1,mod-2)%mod*(pos-i)%mod*a[i]%mod)%mod; 
	}
	cout<<ans<<endl;
	return 0;
}


猜你喜欢

转载自blog.csdn.net/qq_39791208/article/details/79345958
今日推荐