HDU Cut Pieces (计数+求逆元)

题目链接:传送门 

题意:

n个人每个人有n种选择,确定这n个人的排列,使得最后的块数最多。块数是这样算的。1 1 2 算成2块,举一个例子来说明:

1 , 2, 3 

然后根据排列来计算块数, 1 1 1 (1块)1 1 2(2块) 1 1 3(两块)1 2 1 (1块)1,2,3(3块) 1 2 2(两块)

下面来分析如何排列与如何计算总的块数。

分析:

构造排列的时候我们首先要想到这个问题,只考虑相邻的两个元素,他们相同的情况就是min(ai,ai+1),因此我们要使相邻的两个元素相差尽可能的大,因此对原来的序列排序,最小,最大,次小,次大。。。

然后再计算的时候我们从反面考虑,不算重复的情况下的块数为a1*a2*...*an,然后减去重复的块数,重复的块数我们只考虑相邻的两个他们有min(ai,ai+1)种,然后我a1*..ai-1*ai+1*..*an种排列可以使他们重复,然后减去就可以了。

代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>

#define lson num<<1
#define rson num<<1|1
#define gl l,m,lson
#define gr m+1,r,rson
using namespace std;

const int maxn = 1e6+10;

typedef long long LL;

const LL mod = 1e9+7;

LL a[maxn];

LL b[maxn];

LL quick_mod(LL a,LL b){
    LL ans = 1;
    while(b){
        if(b&1) ans = ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ans;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        LL n;
        scanf("%I64d",&n);
        LL ans = 1,fuck;
        for(int i=1;i<=n;i++){
            scanf("%I64d",a+i);
            ans=ans*a[i]%mod;
        }
        fuck = ans;
        sort(a+1,a+n+1);
        int cnt = 1;
        for(int i=1;i<=n/2+1;i++){
            b[cnt++]=a[i];
            b[cnt++]=a[n-i+1];
        }
        ans = ans*n%mod;
        for(int i=2;i<=n;i++){
            LL tmp = fuck*quick_mod(b[i-1]*b[i]%mod,mod-2)%mod;
            ans=(ans-tmp*min(b[i],b[i-1])%mod)%mod;
        }
        printf("%I64d\n",(ans+mod)%mod);
    }
    return 0;
}

/****
555
5
1 2 3 4 5
492
***/


你可能感兴趣的:(HDU Cut Pieces (计数+求逆元))