zoukankan      html  css  js  c++  java
  • lcez校内模拟赛: 小R与苹果派——题解

    题目传送

    首先对两个数组排序。

    然后预处理出数组p[i]表示b[x]<a[i]的最大的x。

    然后我们设f[i][k]表示对于前i个派,我单独选出来k组a[y]>b[y]。(即此时有k组a>b的匹配,其余还未匹配)

    显然f[i][k]=f[i-1][k]+f[i-1][k-1]*(p[i]-(k-1))。等号右边的第一项相当于考虑a[i]不分配b,第二项相当于a[i]分配b。

    这里还要注意一下f[0][0]=f[1][0]=f[2][0]=...=ff[n][0]=1的边界条件。

    但是这个数组肯定不是答案。因为这里f[i][k]中保证了只考虑到A的前i个,B的所有位置,并且满足只给A>B的k个A分配了B, 其余A和B没有配对。

    我们可以再设g[i]表示对于前n个派,恰好有i组a[x]>b[x]的方案数。

    借助容斥原理思考一番后,可得转移方程:

      g[i]=f[n][i]*(n-i)!-  g[j]*c(j,i)   (i+1<=j<=n,c是组合数)。

    这里等号右边的第一项相当于只分配了B的i个A的方案数*没分配B的(n-i)个A分配B的方案数(阶乘项)。这是是所有a>b的匹配对数>=i对的方案数,但注意这里可能会出现同一种分配多次出现的情况(比如3个位置,1、2分配了1、2  ,  3对应3;1、3分配了1、3  ,  2对应2),所以要减掉的g[j]还要乘上个组合数来减掉重复出现的方案数。

    考虑最终答案是什么。“A 做的苹果派比 B 做的苹果派美味的天数比 B 做的比 A 做的美味的天数恰好多 k。”设A 做的苹果派比 B 做的苹果派美味的天数为x, B 做的比 A 做的美味的天数为y。则有方程组:

    x+y=n;

    x-y=k;

    解得x=(n+k)/2

    由此知道的答案即为g[(n+k)/2],同时知道当(n+k)为奇数时是无解的。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 
     5 using namespace std;
     6 
     7 const int N=2005;
     8 
     9 typedef long long LL;
    10 
    11 const LL mod=1e9+9;
    12 
    13 int n,k,a[N],b[N],p[N],s;
    14 
    15 LL jc[N],f[N][N],g[N],c[N][N];
    16 
    17 char ch;
    18 
    19 inline int read()
    20 {
    21     int x=0;
    22     ch=getchar();
    23     while(!isdigit(ch)) 
    24         ch=getchar();
    25     while(isdigit(ch))
    26         x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    27     return x;
    28 }
    29 
    30 inline void init()
    31 {
    32     jc[0]=jc[1]=1;
    33     for(int i=2;i<=n;++i)
    34         jc[i]=jc[i-1]*i%mod;
    35     c[0][0]=1;
    36     for(int i=1;i<=n;++i)
    37     {
    38         c[i][0]=1;
    39         for(int j=1;j<=i;++j)
    40             c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
    41     }
    42 }
    43 
    44 int main()
    45 {
    46     n=read(),k=read();
    47     for(int i=1;i<=n;++i)
    48         a[i]=read();
    49     for(int i=1;i<=n;++i)
    50         b[i]=read();
    51     if((n+k)&1)
    52     {
    53         cout<<0;
    54         return 0;
    55     }
    56     s=(n+k)>>1;
    57     sort(a+1,a+1+n);
    58     sort(b+1,b+1+n);
    59     int las=0;
    60     for(int i=1;i<=n;++i)
    61     {
    62         while(b[las+1]<a[i]&&las+1<=n)
    63             las++;
    64         p[i]=las;
    65     }
    66     init();
    67     f[0][0]=1;
    68     for(int i=1;i<=n;++i)
    69     {
    70         f[i][0]=1;
    71         for(int j=1;j<=i;++j)
    72             f[i][j]=(f[i-1][j]+f[i-1][j-1]*(p[i]-j+1))%mod;
    73     }
    74     g[n]=f[n][n];
    75     for(int i=n-1;i>=s;--i)
    76     {
    77         g[i]=f[n][i]*jc[n-i]%mod;
    78         for(int j=i+1;j<=n;++j)
    79             g[i]=(g[i]-g[j]*c[j][i])%mod;
    80         if(g[i]<0)
    81             g[i]+=mod;
    82     }
    83     printf("%lld",g[s]);
    84     return 0;
    85 }
    AC代码

    这个DP在考场上几乎没有人写出来。为什么这么难?我再次略总结一下:

    1、这个DP出现了不只一个转移方程,即一个转移方程解决不了这个问题, 必须要分步处理,每一步都是个DP。我们做这个题,要考虑怎么分步、步骤之间的联系、每步的处理方式。而这就很难想了。

    2、用到了容斥原理的数学知识,对数学基础不行的同学(尤其是作者)极为不友好。

  • 相关阅读:
    GitHub 源码,Framework 框架
    转int啥啥啥的
    查看apk签名 和 keystore 的信息
    一次apk打开时报内存溢出错误,故写下内存溢出的各种原因和解决方法
    客户端传值里面包含URL特殊字符的应对方法
    Linux全硬盘搜索文件名是xxxxx的命令
    pythonmysql运行报错解决过程中遇到的其中一个报错解决文章来源
    linux查看硬盘占用情况
    Linux使用nginx反向代理。可实现域名指向特定端口
    Linux下使用yum安装软件命令
  • 原文地址:https://www.cnblogs.com/InductiveSorting-QYF/p/11830486.html
Copyright © 2011-2022 走看看