hdu4779 组合计数+dp

提交

题意:给了n*m的网格,然后有p个重型的防御塔,能承受1次攻击,q个轻型防御塔不能接受任何攻击,然后每个防御搭会攻击他所在的行和所在的列,最后求在这个网格上放至少一个防御塔的方案数,

我们枚举 选取多少个重型防御塔然后这个重型防御塔有多少是两个在一行,和两个在一列 O(P^3)的效率

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string.h>
#include <vector>
using namespace std;
typedef long long LL;
const LL MOD=1000000007;
const int maxn=202;
LL C[maxn][maxn];//组合数
LL light[maxn][maxn][maxn];// light[i][j][k]k栈轻量级的防御塔放在i行j列的矩阵的方案数
LL heavy[maxn];// haeavy[i] 表示有i个两个同一行的重量级的方案数
LL Nt[maxn];//N! n阶乘
void init()
{
    Nt[0]=1;
    for(LL i=1; i<=200; i++)
        Nt[i]=(Nt[i-1]*i)%MOD;
    memset(C,0,sizeof(C));
    C[0][0]=1;
    for(int i=1; i<=200; i++)
    {
        C[i][0]=1;
        for(int j=1; j<=i; j++)
        C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
    }
    for(int i=1; i<=200; i++)
        for(int j=1; j<=200; j++)
        {
             int kM=min(i,j);
             light[ i ][ j ][ 0 ] = 1;
             for(int k=1; k<=kM; k++)
             {
                light[ i ][ j ][ k ]=( ( (C[ i ][ k ]*C[ j ][ k ])%MOD )*Nt[k])%MOD;
             }
        }
    for(int i=1; i<=200; i++)
        for(int j=1; j<=200; j++)
        {
             int kM=min(i,j);
             for(int k=1; k<=kM; k++)
                light[i][j][k]=(light[i][j][k-1]+light[i][j][k])%MOD;
        }
    heavy[0]=1;
    for(int i=1; i<=100; i++)
    {
        LL ans=1;
        for(int j=i*2; j>2; j-=2 )
        {
            ans=(C[j][2]*ans)%MOD;
        }
        heavy[i]=ans;
    }
}

int main()
{
    init();
    int N,M,P,Q;
    int cas;
    scanf("%d",&cas);
    for(int cc=1; cc<=cas; cc++)
    {
        scanf("%d%d%d%d",&N,&M,&P,&Q);
         LL ans=0;
         for(int k=1; k<=P; k++)
            for(int i=0; i<=k; i+=2)
              for(int j=0; j+i<=k; j+=2)
              {
                 int LN=N-i/2-j;
                 int LM=M-j/2-i;
                 if(min(LN,LM)<k-(i+j) )continue;
                 LN=N,LM=M;
                 LL d=1;
                 d=( ( ( C[LN][i/2]*C[LM][i] )%MOD )*heavy[i/2])%MOD;
                 LN-=i/2; LM-=i;
                 d=( d*( ( ( ( C[LN][j] * C[LM][j/2] )%MOD ) * heavy[j/2] )%MOD ) )%MOD;
                 LN-=j;
                 LM-=j/2;
                 int lest=k-(i+j);
                 d= ( ( ( d*( ( C[LN][lest]*C[LM][lest] )%MOD ))%MOD)*Nt[lest] )%MOD;
                 LN-=lest;LM-=lest;
                 if(LN>0&&LM>0)
                 {
                     int ge=min(min(LN,LM),Q);
                     d=(d*(light[LN][LM][ge]))%MOD;
                 }
                 ans=(ans+d)%MOD;
              }
        if(Q>0)
        {
            int ge=min(min(N,M),Q);
            ans=(ans+light[N][M][ge])%MOD;
            ans=(ans-1+MOD)%MOD;
        }
        printf("%I64d\n",ans%MOD);
    }
    return 0;
}

时间: 2024-10-04 04:06:02

hdu4779 组合计数+dp的相关文章

HDU 4832 组合计数dp

Chess Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 509    Accepted Submission(s): 198 Problem Description 小度和小良最近又迷上了下棋.棋盘一共有N行M列,我们可以把左上角的格子定为(1,1),右下角的格子定为(N,M).在他们的规则中,"王"在棋盘上的走法遵循十字

[ZJOI2010]排列计数 (组合计数/dp)

[ZJOI2010]排列计数 题目描述 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值 输入输出格式 输入格式: 输入文件的第一行包含两个整数 n和p,含义如上所述. 输出格式: 输出文件中仅包含一个整数,表示计算1,2,?, 的排列中, Magic排列的个数模 p的值. 输入输出样例 输入样例#1: 20 23 输出样例#1: 16 说明

Singer House CodeForces - 830D (组合计数,dp)

大意: 一个$k$层完全二叉树, 每个节点向它祖先连边, 就得到一个$k$房子, 求$k$房子的所有简单路径数. $DP$好题. 首先设$dp_{i,j}$表示$i$房子, 分出$j$条简单路径的方案数, 那么最终答案就为$dp_{i,1}$. 考虑两棵$i-1$房子转移到$i$房子的情况, 分四种情况. 两个子树间不与根节点连边, 那么$dp_{i,j+k}=\sum dp_{i-1,j}dp_{i-1,k}$ 两个子树只有一条路径与根节点连边, $dp_{i,j+k}=\sum dp_{i-

【BZOJ】4559: [JLoi2016]成绩比较 计数DP+排列组合+拉格朗日插值

[题意]n位同学(其中一位是B神),m门必修课,每门必修课的分数是[1,Ui].B神碾压了k位同学(所有课分数<=B神),且第x门课有rx-1位同学的分数高于B神,求满足条件的分数情况数.当有一位同学的一门必修课分数不同时视为两种情况不同.n,m<=100,Ui<=10^9. [算法]计数DP+排列组合+拉格朗日插值 [题解]把分数作为状态不现实,只能逐门课考虑. 设$f[i][j]$表示前i门课,有j个同学被碾压的情况数,则有: $$f[i][j]=g(i)\cdot\sum_{k=j

3.29省选模拟赛 除法与取模 dp+组合计数

LINK:除法与取模 鬼题.不过50分很好写.考虑不带除法的时候 其实是一个dp的组合计数. 考虑带除法的时候需要状压一下除法操作. 因为除法操作是不受x的大小影响的 所以要状压这个除法操作. 直接采用二进制状压是不明智的 2的个数最多为13个 2^13也同样到达了1e4的复杂度. 考虑 hash状压 即 2的个数有x个 那么我们就有状态w表示2还有x个. 这样做的原因是把一些相同的东西给合并起来 而并非分散开来.即有多个2直接记录有多少个即可. 可以发现 这样做不同的除数最多只有5个 状态量较

Yue Fei&#39;s Battle(组合计数递推)

//求一个直径为 k 的树有多少种形态,每个点的度不超过 3 // 非常完美的分析,学到了,就是要细细推,并且写的时候要细心 还有除法取模需要用逆元 #include <iostream> #include <stdio.h> #include <string.h> #include <math.h> #include <stdlib.h> using namespace std; #define MOD 1000000007 #define L

bzoj 1004 Cards 组合计数

这道题考察的是组合计数(用Burnside,当然也可以认为是Polya的变形,毕竟Polya是Burnside推导出来的). 这一类问题的本质是计算置换群(A,P)中不动点个数!(所谓不动点,是一个二元组(a,p),a∈A,p∈P ,使得p(a)=a,即a在置换p的作用后还是a). Polya定理其实就是告诉了我们一类问题的不动点数的计算方法. 对于Burnside定理的考察,我见过的有以下几种形式(但归根结底还是计算不动点数): 1.限制a(a∈A)的特点,本题即是如此(限制了各颜色个数,可以

HDU4815/计数DP

题目链接[http://acm.hdu.edu.cn/showproblem.php?pid=4815] 简单说一下题意: 有n道题,每到题答对得分为a[ i ],假如A不输给B的最小概率是P,那么A最少要得到多少分. 解题过程: 假设有n道题,每个题有两个状态,胜或者败,假设达到某个分数m有k(计数DP)种方式,那么最后是这个分数的概率是k/pow(2,n).那么A不输给B的概率就是小于等于m的分数的概率和. #include<bits/stdc++.h> const int maxn =

[sdut]2879计数dp……或者递推

第五届省赛C:colourful cupcakes N=60. 天真如我,居然在考虑搜索的算法/(ㄒoㄒ)/~~三叉树……3^60=10^24+……不计算考虑复杂度都是耍流氓>_< 再算了一下,感觉O(N^4)可以试试,60^4=10^8+……但是毕竟最差的情况嘛>_<,再看一下题解果然是4重循环的……计数……dp……(想到之前坚信的搜索不禁(*/ω\*)) 中间看到了一个三次动规六个方程的算法. 做麻烦了. 学长思路好快. #include<iostream> #in