• bzoj3622-已经没有什么好害怕的的了


    题意

    给出两个长度为 (n) 的数列 (a,b)(2n) 个数都互不相同,求有多少种对应方式使得 (a_i>b_i) 的个数比 (a_i<b_i) 的个数恰好多 (k)(nle 2000)

    分析

    容易把问题转化成有多少种对应方案使得 (a_i>b_i) 的个数恰好多 (m) 。这是一个序列上的计数问题,一种经典的思路是分阶段考虑。

    首先给 (a) 排序,预处理出 (b) 中有多少个数比 (a_i) 小,记为cnt[i] 。分阶段考虑,设 f[i][j] 表示给 (a) 的前 (i) 个分配 (j) 个小于它们的的方案数。那么有转移:

    [f[i][j]=f[i-1][j]+f[i-1][j-1]*(cnt[i]-(j-1)) ]

    除了这 (j) 个以外其他是随便选的。这就导致了计算重复,所以我们考虑如何减去重复。设 (g_i) 表示整个对应中恰好有 (i) 个小于它们的,那么有:

    [g[i]=f[n][i](n-i)!-重复 ]

    这里最妙的方法是,重复我们反过来求。重复的是什么呢?就是随便匹配的过程中得到的那些 (a_i>b_i) 的方案。这些方案在 (g[k],k>i) 中是包含的!!(g_k) 中每一个方案被 (f[n][i]) 算多了 (inom k i) 次.

    [g[i]=f[n][i](n-i)!-sum _{i<kle n}inom k ig[k] ]

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long giant;
    inline char nchar() {
    	static const int bufl=1<<20;
    	static char buf[bufl],*a,*b;
    	return a==b && (b=(a=buf)+fread(buf,1,bufl,stdin),a==b)?EOF:*a++;
    }
    inline int read() {
    	int x=0,f=1;
    	char c=nchar();
    	for (;!isdigit(c);c=nchar()) if (c=='-') f=-1;
    	for (;isdigit(c);c=nchar()) x=x*10+c-'0';
    	return x*f;
    }
    const int maxn=2e3+1;
    const int q=1e9+9;
    inline int Plus(int x,int y) {return ((giant)x+(giant)y)%q;}
    inline int Sub(int x,int y) {return Plus(x,q-y);}
    inline int Multi(int x,int y) {return (giant)x*y%q;}
    inline int mi(int x,int y) {
    	int ret=1;
    	for (;y;y>>=1,x=Multi(x,x)) if (y&1) ret=Multi(ret,x);
    	return ret;
    }
    inline int inv(int x) {return mi(x,q-2);}
    int a[maxn],b[maxn],f[maxn],g[maxn],le[maxn],fac[maxn],ifac[maxn];
    inline int C(int n,int m) {return Multi(Multi(fac[n],ifac[m]),ifac[n-m]);}
    int main() {
    #ifndef ONLINE_JUDGE
    	freopen("test.in","r",stdin);
    #endif
    	int n=read();
    	int k=read();
    	if ((n+k)&1) puts("0"),exit(0);
    	fac[0]=ifac[0]=fac[1]=ifac[1]=1;;
    	for (int i=2;i<=n;++i) ifac[i]=inv(fac[i]=Multi(fac[i-1],i));
    	for (int i=1;i<=n;++i) a[i]=read();
    	for (int i=1;i<=n;++i) b[i]=read();
    	sort(a+1,a+n+1),sort(b+1,b+n+1);
    	for (int i=1,j=0;i<=n;++i) {
    		for (;j<n && b[j+1]<a[i];++j);
    		le[i]=j;
    	}
    	f[0]=1;
    	for (int i=1;i<=n;++i) for (int j=i;j;--j) f[j]=Plus(f[j],Multi(f[j-1],max(le[i]-j+1,0)));
    	g[n]=f[n];
    	for (int i=n-1;i;--i) {
    		int &gi=g[i]=0;
    		for (int j=i+1;j<=n;++j) gi=Plus(gi,Multi(g[j],C(j,i)));
    		gi=Sub(Multi(f[i],fac[n-i]),gi);
    	}
    	printf("%d
    ",g[(n+k)>>1]);
    	return 0;
    }
    
  • 相关阅读:
    检查你的iOS程序是否正在被调试
    破解从 AppStore 下载的 IPA
    在 iOS 中如何发短信
    关于移动应用UI部分管理的一些思考
    在 iOS 应用中实现飞行模式提醒
    如何在iOS应用中拨打电话,并让用户确认
    [转]How to hide inputAccessoryView without dismissing keyboard
    php 读取文件并以文件方式下载
    一个session,判断用户唯一的技巧
    一个简单文件上传代码
  • 原文地址:https://www.cnblogs.com/owenyu/p/7375281.html
Copyright © 2020-2023  润新知