Codeforces 938E Max History:排列 + 逆元【考慮單個元素的貢獻】

Leohh發表於2018-02-26

題目連結: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 }

 

相關文章