BZOJ 3294: [Cqoi2011]放棋子

3294: [Cqoi2011]放棋子

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 628  Solved: 238
[Submit][Status][Discuss]

Description

Input

输入第一行为两个整数nmc,即行数、列数和棋子的颜色数。第二行包含c个正整数,即每个颜色的棋子数。所有颜色的棋子总数保证不超过nm

Output

输出仅一行,即方案总数除以 1,000,000,009的余数。

Sample Input

4 2 2

3 1

Sample Output

8

HINT

N,M<=30 C<=10 总棋子数<=250

Source

分析:

上课不好好听课的我TAT...

此题最重要的思想感觉是补集转化思想...

f[i][j][k]代表前k种颜色占据了i行j列的方案数,那么怎么转移...

f[i][j][k]=Σf[i-x][j-y][k-1]*g[x][y][k]*c[i][x]*c[j][y]

g[x][y][k]代表什么?第k种颜色刚好占据了x行y列...感觉这个转移还是很好想的...

但是问题来了...g[x][y][k]怎么求...

我们可以转化为总方案数减去不合法的方案数,也就是g[i][j][k]=c[i*j][num[k]]-Σg[x][y][k]*c[i][x]*c[j][y]...

注意边界...WA了好几次...QAQ...

代码:

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 //by NeighThorn
 6 #define int long long
 7 using namespace std;
 8
 9 const int maxn=30+5,MOD=1e9+9;
10
11 int n,m,co,ans,num[maxn],c[maxn*maxn][maxn*maxn],f[maxn][maxn][maxn],g[maxn][maxn][maxn];
12
13 signed main(void){
14     memset(f,0,sizeof(f));
15     memset(g,0,sizeof(g));
16     scanf("%lld%lld%lld",&n,&m,&co);
17     for(int i=1;i<=co;i++)
18         scanf("%lld",&num[i]);
19     for(int i=0;i<=900;i++)
20         c[i][0]=c[i][i]=1;
21     for(int i=2;i<=900;i++)
22         for(int j=1;j<i;j++)
23             c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD;
24     for(int k=1;k<=co;k++)
25         for(int i=1;i<=n;i++)
26             for(int j=1;j<=m;j++)
27                 if(i*j>=num[k]&&max(i,j)<=num[k]){
28                     g[i][j][k]=c[i*j][num[k]];
29                     for(int x=1;x<=i;x++)
30                         for(int y=1;y<=j;y++)
31                             if((i-x)||(j-y))
32                                 g[i][j][k]=(g[i][j][k]-g[x][y][k]*c[i][x]%MOD*c[j][y]%MOD+MOD)%MOD;
33                 }
34     f[0][0][0]=1;
35     for(int k=1;k<=co;k++)
36         for(int i=1;i<=n;i++)
37             for(int j=1;j<=m;j++)
38                 if(i*j>=num[k]){
39                     for(int x=1;x<=i;x++)
40                         for(int y=1;y<=j;y++)
41                             (f[i][j][k]+=f[i-x][j-y][k-1]*g[x][y][k]%MOD*c[i][x]%MOD*c[j][y]%MOD)%=MOD;
42                 }
43     for(int i=1;i<=n;i++)
44         for(int j=1;j<=m;j++)
45             (ans+=f[i][j][co]*c[n][i]%MOD*c[m][j]%MOD)%=MOD;
46     printf("%lld\n",ans);
47     return 0;
48 }



by NeighThorn

时间: 2024-08-10 15:07:37

BZOJ 3294: [Cqoi2011]放棋子的相关文章

P3158 [CQOI2011]放棋子(dp+组合数)

P3158 [CQOI2011]放棋子 放棋子的顺序和方案数无关,所以可以从按颜色递推 设$f[u][p][k]$为放到第$u$种颜色,所剩空间$p*k$的方案数 $g[u][i][j]$表示第$u$种颜色占据$i*j$空间的方案数,可以预处理 $g[u][i][j]=\binom{i*j}{c[u]}-\sum_{p=1}^{i}\sum_{k=1}^{j}g[u][p][k]*\binom{i}{i-p}*\binom{j}{j-k}*[p<i||j<k]$ $f[u][p][k]=\su

[BZOJ 4563][Haoi2016]放棋子(错排公式)

Description 给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列,要求你在这个矩阵上放N枚棋子(障碍的位置不能放棋子),要求你放N个棋子也满足每行只有一枚棋子,每列只有一枚棋子的限制,求有多少种方案. Solution 其实和给出的障碍没什么关系,可以直接上错排公式(因为一开始的障碍可以看做一个排列,然后放棋子等同于要你生成一个新的不能有和原来位置相同元素的排列) f[n]=(f[n-1]+f[n-2])*(n-1)(为什么的话上一篇里有说Qw

[题解] [CQOI2011] 放棋子

题面 题解 为了练习计数而做 注意到一种颜色占据的行, 列其他的颜色不能放 又考虑到我们并不需要知道哪些行哪些列选了, 只需要知道还有几行几列没选即可 于是有 \(f[i][j][k]\) 代表前 \(i\) 种颜色选完之后, 还有 \(j\) 行没选, \(k\) 列没选的方案数 \(g[i][j][k]\) 代表, \(i\) 个棋子, 放在 \(j\) 行 \(k\) 列中并且没有空行空列的方案数 \(cnt_i\) 代表颜色为 \(i\) 的棋子有几个 有 \[ \displaystyl

[CQOI2011] 放棋子 - 计数dp

在一个 \(m\) 行 \(n\) 列的棋盘里放一些彩色的棋子,使得每个格子最多放一个棋子,且不同颜色的棋子不能在同一行或者同一列,有多少种方法? Solution 设 \(f[i][j][k]\) 表示用前 \(k\) 种颜色的棋子,占领了 \(i\) 行 \(j\) 列的方案数 设 \(g[i][j][k]\) 表示用任意 \(k\) 个同色棋子占领 \(i\) 行 \(j\) 列的方案数,则考虑总方案数 - 实际上有没有被占领的行或列的方案数,则 \[g[i][j][k]=C_{ij}^k

[CQOI2011]放棋子

想到了50%吧算是. f[i][j][k]表示,前i种,占了j行k列.方案数. 发现,转移要处理:"用c个棋子,占据n行m列"的方案数. 设g[i][j][k]表示,i行j列用k个棋子占的方案数.直接处理复杂度爆炸. 然后我就mengbier了. 考虑大力容斥: 也即,总方案数-不合法方案数(不能覆盖完全) g[i][j][k]=C(i*j,k)-∑l∑r:g[l][r][k]*C(i,l)*C(j,r) (i*j>=k&&l<=i&&j&l

bzoj 3519: [Zjoi2014] 消棋子 题解

[序言]在大家怀疑的眼光下,我做了一个中午和半个下午.调了一个晚上的题目总算A了! [原题] 消棋子是一个有趣的游戏.游戏在一个r * c的棋盘上进行.棋盘的每个格 子,要么是空,要么是一种颜色的棋子.同一种颜色的棋子恰好有两个.每一轮, 玩家可以选择一个空格子(x, y),并选择上下左右四个方向中的两个方向,如果 在这两个方向上均存在有棋子的格子,而且沿着这两个方向上第一个遇到的棋子 颜色相同,那么,我们将这两个棋子拿走,并称之为合法的操作.否则称这个操 作不合法,游戏不会处理这个操作.游戏的

bzoj4563[Haoi2016]放棋子

bzoj4563[Haoi2016]放棋子 题意: 给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列.要求你放N枚棋子(障碍的位置不能放棋子),也满足每行只有一枚棋子,每列只有一枚棋子的限制,求有多少种方案.N≤200. 题解: 发现在障碍在什么位置和答案无关.因此可以把棋子摆成左上-右下对角线,然后就是错排问题(一个长度为n的排列,要求每个元素不能放在与自己编号相同的位置上,问有多少种方案满足条件)了.错排公式:f[i]=(f[i-1]+f[i-2]

主席树初探 &amp; bzoj 3295: [Cqoi2011] 动态逆序对 题解

[原题] 3295: [Cqoi2011]动态逆序对 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 778  Solved: 263 [Submit][Status] Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元

LibreOJ #2061. 「HAOI2016」放棋子

二次连通门 : LibreOJ #2061. 「HAOI2016」放棋子 /* LibreOJ #2061. 「HAOI2016」放棋子 MDZZ ... 错排 + 高精 */ #include <iostream> #include <cstdio> #include <vector> #include <iomanip> #include <cassert> #include <algorithm> #define int64 l