度度熊与组题 组合数+DP

度度熊与组题

沃老师在出比赛的题目时遇到麻烦啦!

遇到的麻烦如下:

现在沃老师手上有 2n 道题,题目编号由 1∼2n,已知第 i 道题难度为 ai,这些题的难度还满足当 i<j 时 ai≤aj。现在沃老师想把这些题目分在两套比赛上,每套比赛会被分到 n 道题,每道题都会恰出现在其中一场比赛中。假设分配完后,第一套题难度第 i 小的题的难度为 ci (第 i 小是指不去重的的第 i 小,例如有四道题难度分别是 1,1,2,3 时,难度第 3 小的题是难度是 2),第二套题难度第 i 小的题为 di,沃老师定义两场比赛的难度相似度为 ∑ni=1|ci−di|,且沃老师希望分配完题目后,两场比赛的难度相似度尽可能的小。

看到这你可能会觉得这算什么麻烦,难度相似度的最小值不就是 ∑ni=1(a2i−a2i−1) 嘛?

是的,光是要使难度相似度最小并不构成沃老师的麻烦,但沃老师是个好奇宝宝,他还想知道,有多少种分配题目的方式,能使难度相似度最小呢?这个问题可能就没那么简单了。

于是沃老师就来拜托聪明的度熊帮他解决他心中的困惑,各位也帮忙算算吧。

请输出分配题目的方式数量除以 109+7 的余数。

两种分配方式 A 和 B 不同当且仅当存在某道题出现在 A 的第一套比赛中,却没有出现在 B 的第一套比赛中。举例来说,当 n=1 时,第一套比赛含有题目 1 第二套比赛含有题目 2 和 第一套比赛含有题目 2 第二套比赛含有题目 1 是不同的。


一个很好的引理: 最优解 对于 (<=v-1)(<=v) 的 个数绝对值差不超过1
然后我们就可以枚举分配然后DP

#include<bits/stdc++.h>
using namespace std;
#define maxnn 4000
#define mod 1000000007
#define ll long long 
ll f[maxnn];
ll sum[maxnn];
ll dp[maxnn];
ll T,n;
ll a[maxnn];
ll num[maxnn];
ll ksm(int a,int b)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
ll  C(int n,int m)
{
    if(n==m||m==0) return 1;
    ll A=1,B=1;
    for(int i=0;i<m;i++)
    {
        B*=(i+1)%mod;
        A=A*(n-i)%mod;
    }
    return A*ksm(B,mod-2);
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d",&n);
        int up=n<<1;
        for(int i=1;i<=up;i++){
            scanf("%d",&a[i]);
            num[i]=0;
        } 
        for(int i=1;i<=up;i++) num[a[i]]++;
        dp[0]=1;
        ll sum=0;
        for(int i=1;i<=up;i++){
            if((sum&1)==0&&(num[i]&1)==0){ 
                dp[i]=dp[i-1]*C(num[i],num[i]>>1)%mod; 
            }
            else if((sum&1)==0&&(num[i]&1)==1){
                dp[i]=dp[i-1]*C(num[i],num[i]>>1)*2%mod; 
            }
            else if((sum&1)==1&&(num[i]&1)==1){
                dp[i]=dp[i-1]*C(num[i],num[i]>>1)%mod; 
            }
            else { 
                dp[i]=dp[i-1]*C(num[i],num[i]>>1)%mod;
                if(num[i]){
                    dp[i]+=dp[i-1]*C(num[i],(num[i]-2)>>1)%mod;
                    dp[i]%=mod;
                }  
            }
            sum+=num[i];
        }
        printf("%lld
",dp[up]);
    } 
    return 0;
}
刀剑映出了战士的心。而我的心,漆黑且残破
原文地址:https://www.cnblogs.com/OIEREDSION/p/11434287.html