• bzoj 3622 已经没有什么好害怕的了 类似容斥,dp


    3622: 已经没有什么好害怕的了

    Time Limit: 10 Sec  Memory Limit: 256 MB
    Submit: 1213  Solved: 576
    [Submit][Status][Discuss]

    Description

    Input

    Output

    Sample Input

    4 2
    5 35 15 45
    40 20 10 30

    Sample Output

    4

    HINT


    输入的2*n个数字保证全不相同。


    还有输入应该是第二行是糖果,第三行是药片

    Source

    2014湖北省队互测week2

    先把两个数列a,b从小到大排序。

    设f[i][j]表示前i对数中,满足糖果大于药片的对数“至少”有j对的方案数。

    总共要组n对数,其中糖果大于药片的组数比药片大于糖果的组数多k组,那么总共需要 m=(n+k)/2 对数满足糖果大于药片,剩下的满足药片大于糖果。

    先用next[]数组记录对于a中的每个数,在j中有next[i]个数比它小,则a[i]可以和next[i]个数组成糖果大于药片的数对。

    进行DP: f[i][j]=(f[i-1][j]+f[i-1][j-1]*max(next[i]-(j-1),0)%mod

    这样求出来的是“至少”有j对的方案数,而我们需要的是“恰好”有m对的方案数。

    显然(并不)这是一个容斥问题:

    f[n][i]表示总共n对数中,至少有i对数满足“糖果大于药片”。在a数列中,剩下的(n-i)个数各自要找b数列中剩下的一个数配对,方案共有 (n-i)!  (←阶乘) 种。

    设dp[n][i]=“n对数中恰好i对数满足糖果大于药片”的方案数,则dp[n][i] = f[n][i]*(n-i)! -多余部分

    接下来分析多余部分:

    若j>i,在dp[n][j]中(这里的dp[n][j]是已经算完的正确答案,为此需要倒序计算)的一部分可能会被算进f[n][i]中,这种误算的方案有C[j][i]*dp[n][j]种。

    所以: dp[n][i]=f[n][i]*(n-i)! - ( Σ(i<j<=n)   C[j][i]*dp[n][j] )  注意取模

    之后发现dp数组只用到了[n][i],所以第一维可以扔掉了

    十分巧妙,倒着去推出,估计看到数据范围我也不会去想这样的方法。

    容斥一般20还行,这么多貌似(⊙o⊙)…

     1 #pragma GCC optimize(2)
     2 #pragma G++ optimize(2)
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<cmath>
     6 #include<cstdio>
     7 #include<cstring>
     8 
     9 #define ll long long
    10 #define N 2007
    11 #define mod 1000000009
    12 using namespace std;
    13 inline int read()
    14 {
    15     int x=0,f=1;char ch=getchar();
    16     while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    17     while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    18     return x*f;
    19 }
    20 
    21 int n,m;
    22 int a[N],b[N];
    23 int num[N];
    24 ll f[N][N],dp[N],C[N][N],A[N];
    25 
    26 void init_C()
    27 {
    28     for (int i=0;i<=n;i++)C[i][0]=1;
    29     for (int i=1;i<=n;i++)
    30         for (int j=1;j<=i;j++)
    31             C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    32     A[1]=1;
    33     for (int i=2;i<=n;i++)(A[i]=A[i-1]*i)%=mod;
    34 }
    35 int main()
    36 {
    37     n=read(),m=(read()+n)/2;
    38     for (int i=1;i<=n;i++)a[i]=read();
    39     for (int i=1;i<=n;i++)b[i]=read();
    40     sort(a+1,a+n+1),sort(b+1,b+n+1);
    41     for (int i=1,j=1;i<=n;i++)
    42     {
    43         while(j<=n&&b[j]<a[i])j++;
    44         num[i]=j-1;
    45     }
    46     init_C();
    47     for (int i=0;i<=n;i++)f[i][0]=1;
    48     for (int i=1;i<=n;i++)
    49         for (int j=1;j<=i;j++)
    50             f[i][j]=(f[i-1][j]+f[i-1][j-1]*max(num[i]-j+1,0))%mod;
    51     for (int i=n;i>=m;i--)
    52     {
    53         dp[i]=f[n][i]*A[n-i]%mod;
    54         for(int j=i+1;j<=n;j++)
    55             dp[i]=((dp[i]-dp[j]*C[j][i]%mod)%mod+mod)%mod;
    56     }
    57     printf("%lld
    ",dp[m]);
    58 }
  • 相关阅读:
    vector向量容器的一些基本操作
    OpenCV鼠标画图例程,鼠标绘制矩形
    网络安全学习笔记--《暗战强人:黑客攻防入门全程图解》
    遗传算法解决TSP问题实现以及与最小生成树的对比
    基于opencv的手写数字字符识别
    最小生成树
    opencv基本图像操作
    使用vs2010 opencv2.4.4编译release版本程序
    【machine translate】deep learning seq2seq
    【DL】物体识别与定位
  • 原文地址:https://www.cnblogs.com/fengzhiyuan/p/8487008.html
Copyright © 2020-2023  润新知