[JZOJ4924] 向再见说再见

Description

这里写图片描述
这里写图片描述

Solution

先别管分差,考虑这个分差算出来第一个队分别得多少分。

此处有一个极其精妙的 dp
先把两个队从小到大排序。
f[i][j] 表示做到第 i 个人,第一队赢了j场,输的不算入答案的方案数。

这个明显DP
做完后,剩下 nj 个人,那么设 g[j]=f[n][j]×(nj)! ,剩下的人随便打。

那么 g[i] 就是至少赢了 j 场。
h[i]表示刚好赢 i 场(就是答案)
那么h[i]=g[i]g[i+1]

答案是错的。
为什么?因为有重复。
1表示赢,X表示不清楚
我们只看后三位的 f[4][2]
X1X1
X11X
XX11
转移出 g ,第一位暂时不管。
X111
X111
X111
h[3]=1 ,第一位不管。
三种情况都有可能打出同一种方案,所以

h[i]=g[i]j>ih[j]×??

观察上面,??不就是 Cjij 么,因为剩下的所有 ji 个都有可能打出同一种。
所以

h[i]=g[i]j>ih[j]×Cjij=g[i]j>ih[j]×Cij

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 2005
#define mo 1000000007
#define LL long long 
using namespace std;
int n,m;
LL ans,a[N],b[N],f[N][N],g[N],h[N],js[N];
LL ksm(LL k,LL n)
{
    if(n==1) return k%mo;
    if(n==0) return 1;
    LL s=ksm(k,n/2);
    return (n%2)?(s*s%mo*k)%mo:(s*s)%mo;
}
LL zh(LL m,LL n)
{
    return(js[n]*ksm(js[m],mo-2)%mo*ksm(js[n-m],mo-2))%mo;
}
int main()
{
    cin>>n>>m;
    if((n+m)%2!=0) 
    {
        cout<<0;
        return 0;
    }
    int p1=(n+m)/2,p2=(n-m)/2;
    js[0]=1;
    fo(i,1,n) scanf("%lld",&a[i]),js[i]=js[i-1]*i%mo;
    fo(i,1,n) scanf("%lld",&b[i]);
    sort(a+1,a+n+1);
    sort(b+1,b+n+1);
    memset(f,0,sizeof(f));
    LL p=0;
    f[0][0]=1;
    fo(i,1,n)
    {
        while(b[p+1]<a[i]&&p<n) p++;
        fo(j,0,i)
        {
            f[i][j]=f[i-1][j];
            LL j1=j;
            if(j>0&&p>=j) f[i][j]=(f[i][j]+f[i-1][j-1]*(p-j1+1)%mo)%mo;
        }
    }
    fod(i,n,0)
    {
        g[i]=(f[n][i]*js[n-i])%mo;
        h[i]=g[i];
        fo(j,i+1,n) h[i]=(h[i]-h[j]*zh(i,j)%mo+mo)%mo;
    }
    if(p1==p2) cout<<(h[p1]%mo);
    else cout<<(h[p1]+h[p2])%mo;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值