Codeforces 938E Max History:排列 + 逆元【考慮單個元素的貢獻】
題目鏈接:http://codeforces.com/problemset/problem/938/E
題意:
定義f(a):
初始時f(a) = 0, M = 1。
枚舉i = 2 to n,如果a[i] > a[M],那麽f(a) += a[M], M = i。
給定長度為n的數組a,問你它的所有排列的f(a)之和 MOD 1e9+7。
題解:
對於某個確定排列中的一個數a[i],如果所有大於等於a[i]的數都排在a[i]之後,那麽一定ans += a[i]。
所以就要求每個a[i]對於答案的貢獻,相加起來即為總答案。
先將a[i]升序排列。
考慮由所有n個數組成的排列:
總排列數為n!。
僅考慮由大於等於a[i]的數組成的排列:
大於等於a[i]的數共有n-i+1個。
總排列數為(n-i+1)!。
其中a[i]排在最前面的排列有(n-i)!個。
所以由n個數組成,且所有大於等於a[i]的數都排在a[i]之後
這樣的排列的總數為(n-i)! / (n-i+1)! * n!個。
化簡即為n!/(n-i+1)個。
所以a[i]對答案作出的貢獻為:n! / (n-i+1) * a[i]。
所以對於區間[i,nex),如果a[i to nex-1]都相等的話
這個區間對答案做出的總貢獻即為:n! / (n-i+1) * a[i] * (nex-i)
特別地,如果有a[i] == a[n],顯然它對答案的貢獻為0。
另外,對於貢獻中的除以(n-i+1),應該寫成乘inv(n-i+1)。
最後O(n)統計一下就好。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <algorithm> 5 #define MAX_N 1000005 6#define MOD 1000000007 7 8 using namespace std; 9 10 int n; 11 int a[MAX_N]; 12 long long ans=0; 13 14 void exgcd(int a,int b,int &x,int &y) 15 { 16 if(b==0) 17 { 18 x=1; y=0; 19 return; 20 } 21 exgcd(b,a%b,y,x); 22 y-=(a/b)*x; 23 } 24 25 int inv(int a) 26 { 27 int x,y; 28 exgcd(a,MOD,x,y); 29 return (x%MOD+MOD)%MOD; 30 } 31 32 int main() 33 { 34 cin>>n; 35 for(int i=1;i<=n;i++) cin>>a[i]; 36 long long f=1; 37 for(int i=1;i<=n;i++) f=f*i%MOD; 38 sort(a+1,a+1+n); 39 int nex=1; 40 for(int i=1;i<=n;i=nex) 41 { 42 if(a[i]==a[n]) break; 43 while(nex<=n && a[i]==a[nex]) nex++; 44 ans=(ans+f*inv(n-i+1)%MOD*a[i]%MOD*(nex-i)%MOD)%MOD; 45 } 46 cout<<ans<<endl; 47 }
Codeforces 938E Max History:排列 + 逆元【考慮單個元素的貢獻】