10.06 容斥练习T2 DP+容斥原理(二项式反演)
Description
已经使 Modoka 有签订契约, 和自己一起战斗的想法后 , Mami 忽然感到自己不再是孤单一人了呢.
于是, 之前的谨慎的战斗作风也消失了 , 在对 Charlotte 的傀儡使用终曲——Tiro Finale后 , Mami 面临着即将被 Charlotte 的本体吃掉的局面.
这时, 已经多次面对过 Charlotte 的 Homura 告诉了学 OI 的你这样一个性质——Charlotte的结界中有两种具有能量的元素——一种是“糖果” , 另一种是“药片” , 每种各有 n 个. 在Charlotte 发动进攻前, “糖果”和“药片”会两两配对,若恰好“糖果”比“药片”能量大的组数比“药片”比“糖果”能量大的组数多 k 组, 则在这种局面下, Charlotte 的攻击会丢失,从而 Mami 仍有消灭 Charlotte 的可能. 你必须根据 Homura 告诉你的“糖果”和“药片”的能量的信息迅速告诉 Homura 这种情况的个数.
于是, 之前的谨慎的战斗作风也消失了 , 在对 Charlotte 的傀儡使用终曲——Tiro Finale后 , Mami 面临着即将被 Charlotte 的本体吃掉的局面.
这时, 已经多次面对过 Charlotte 的 Homura 告诉了学 OI 的你这样一个性质——Charlotte的结界中有两种具有能量的元素——一种是“糖果” , 另一种是“药片” , 每种各有 n 个. 在Charlotte 发动进攻前, “糖果”和“药片”会两两配对,若恰好“糖果”比“药片”能量大的组数比“药片”比“糖果”能量大的组数多 k 组, 则在这种局面下, Charlotte 的攻击会丢失,从而 Mami 仍有消灭 Charlotte 的可能. 你必须根据 Homura 告诉你的“糖果”和“药片”的能量的信息迅速告诉 Homura 这种情况的个数.
Input
第一行两个整数 n, k, 含义如题目描述.
接着第二行 n 个整数, 第 i 个数表示第 i 个糖果的能量.
第三行 n 个整数, 第 j 个数表示第 j 个药片 的能量.
接着第二行 n 个整数, 第 i 个数表示第 i 个糖果的能量.
第三行 n 个整数, 第 j 个数表示第 j 个药片 的能量.
Output
一个整数, 表示消灭 Charlotte 的情况个数.
答案可能会很大, 所以 mod (1 0^9 + 9)
答案可能会很大, 所以 mod (1 0^9 + 9)
Sample Input
4 2
5 35 15 45
40 20 10 30
Sample Output
4
Hint
正确的组合 (5-40, 35-20, 15-10, 45-30), (5-40, 45-20, 15-10, 35-30), (45-40,5-20, 15-10, 35-30) 以及 (45-40, 35-20, 15-10, 5-30)
【数据规模】
约定:给出的 2*n 个能量值两两不同
对于 10%的数据: 1<=n<=10
对于 40%的数据: 1<=n<=500
对于 100%的数据: 1<=n<=2000, 0<=k<=n
【数据规模】
约定:给出的 2*n 个能量值两两不同
对于 10%的数据: 1<=n<=10
对于 40%的数据: 1<=n<=500
对于 100%的数据: 1<=n<=2000, 0<=k<=n
这题简直是又刷新我的三观
这题是湖北省省队互测的题目,难度已经是非常的大了
首先我们肯定要排序,然后接着一堆666操作就来了
首先我们要计算出一个nxt[i]数组表示在前 i 个数中有多少个数字是比a[i]要小的
我们计算出这个东西之后就可以定义一个状态f[i][j]了,这个状态描述的是前 i 对数字中,如果至少匹配 j 对的方案数
方程就是
1 f[i][j]=(f[i-1][j]+f[i-1][j-1]*(nxt[i]-(j-1)))%mod
为什么是这个样子的?因为我们显然只有选择或者不选择这两种情况,如果选择的话显然只能在前面nxt[i]个数里面选择,但是因为之前已经配对了j-1对,所以选择的数目就要少了很多
如果不选的更简单了,直接继承上一个状态就可以了
(注意,我们在这里只关心配对的数字方案数,至于其他的元素我们其实并不关心,这要到后面其实才能够说的清楚)
然后我们发现,因为我们忽略了其他元素,所以除开f[n][i]中 i 对之外,其实我们还有 n-i 对需要进行排列,这个的方案数其实是(n-i)!的方案数(两两配对固定一组可以证明)
所以我饿哦们其实可以设一个数组g[i]表示考虑n对包括其他无关不知道有没有配对元素(也就是至少i对元素)的方案数,那么我们又可以得到一个方程
1 g[i]=f[n][i]*(n-i)!%mod
然后我们的目标其实是求恰好k个的方案数,显然应该配对(n+k)/2个元素,但是我们其实记录的是至少i对的元素,显然我们就要容斥
对于一个 i 对配对来说,会包含 j (j>i)对,由于我们是恰好,所以我们就要在 j 对里面选择i个元素才是我们的方案数,显然这么下来就是C[i][j]次重复,我们就要逐个减掉
所以我们设一个F[i]数组表示恰好有 i 对符合条件的数对,显然对于 i 来说会被前面的大于 i 的包含,所以减掉C[i][j]*F[j]就可以了
方程就是;
1 F[i]=((g[i]-c[j][i]*F[j]%mod)%mod+mod)%mod;
然后输出答案就可以咯
code:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 long long a[2003],b[2004],nxt[2004]; 6 const long long mod=1e9+9; 7 long long c[3000][3000],jie[3000],n,k,f[3000][3000],g[3000],dp[3000]; 8 void pre(){ 9 c[0][0]=1; 10 jie[0]=1; 11 for(long long i=1;i<=n+1;i++){ 12 c[i][i]=c[i][0]=1; 13 for(long long j=1;j<i;j++){ 14 c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod; 15 } 16 jie[i]=jie[i-1]*i%mod; 17 // cout<<jie[i]<<" "; 18 } 19 } 20 int main(){ 21 cin>>n>>k; 22 pre(); 23 for(long long i=1;i<=n;i++)cin>>a[i]; 24 for(long long i=1;i<=n;i++)cin>>b[i]; 25 sort(a+1,a+n+1);sort(b+1,b+n+1); 26 long long head=1; 27 f[0][0]=1; 28 for(long long i=1;i<=n;i++){ 29 nxt[i]=head; 30 while(a[i]>b[head]&&head<=n){ 31 nxt[i]=head; 32 head++; 33 } 34 f[i][0]=1; 35 } 36 for(long long i=1;i<=n;i++){ 37 for(long long j=1;j<=n;j++){ 38 f[i][j]=(f[i-1][j]+(f[i-1][j-1]*(nxt[i]-j+1))%mod)%mod; 39 // cout<<f[i][j]<<" "; 40 } 41 // cout<<endl; 42 } 43 if((k+n)&1){ 44 cout<<0; 45 return 0; 46 } 47 k=k+n>>1; 48 for(long long i=n;i>=1;i--){ 49 g[i]=f[n][i]*jie[n-i]%mod; 50 for(long long j=i+1;j<=n;j++){ 51 g[i]=((g[i]-c[j][i]*g[j]%mod)%mod+mod)%mod; 52 } 53 } 54 for(int i=1;i<=n;i++)cout<<g[i]<<" "; 55 cout<<(g[k]+mod)%mod; 56 return 0; 57 }
over