首页 > 其他分享 >【题解】P3158 [CQOI2011]放棋子

【题解】P3158 [CQOI2011]放棋子

时间:2022-12-31 07:22:33浏览次数:73  
标签:const dbinom limits int 题解 P3158 棋子 CQOI2011 sum

兄弟们,我起了,一日之计在于晨呐。

题意

P3158 [CQOI2011]放棋子

有一个 \(n\) 行 \(m\) 列的棋盘和 \(c\) 种颜色的棋子,每种棋子有 \(a_i\) 个。

要求不同颜色的棋子不能放在同一行或同一列,问放棋子的方案总数。

对 \(10^9 + 9\) 取模。

\(1 \leq n, m \leq 30, 1 \leq c \leq 10, \sum\limits a_i \leq \max(250, nm)\)

思路

容斥 dp.

可以观察到:对于任意合法方案,交换其中任意两行(或列)依然合法。

所以我们只关心被占用的行数和列数,不需要在意具体选中的行和列。

首先可以想到一个普通的 dp 做法:

设 \(f[i][j][k]\) 表示用前 \(k\) 种颜色占用 \(i\) 行 \(j\) 列的方案总数(注意不一定是 \(i \times j\) 的矩阵)

转移直接枚举最后一种颜色的贡献。

令 \(S(i, j, k)\) 表示用 \(k\) 个同颜色的棋子占用 \(i\) 行 \(j\) 列的方案数,那么转移可以写成:

\(f[i][j][k] = \sum\limits_{p = 1}^i \sum\limits_{q = 1}^j \dbinom{i}{p} \dbinom{j}{q} f[i - p][j - q][k - 1] \cdot S(p, q, a_k)\)

接下来考虑求出 \(S\).

直接做不好做,考虑容斥。

由于行和列实际上是等价的,所以容斥可以这样做:

占满 \(i\) 行 \(j\) 列 = 全部方案 - 至少不占 \(1\) 行/列 + 至少不占 \(2\) 行列 - ...

那么 \(S\) 的转移是:

\(S(i, j, k) = \sum\limits_{p = 0}^i \sum\limits_{q = 0}^j (-1)^{p + q} \dbinom{i}{p} \dbinom{j}{q} \dbinom{(i - p)(j - q)}{k}\)

最后一项的意义是在剩余的 \(i - p\) 行和 \(j - q\) 列中放置 \(k\) 个棋子的方案数,可以直接相乘的原因是平移后可以看成一个 \((i - p) \times (j - q)\) 的矩阵。

于是最终的答案是:

\(\sum\limits_{i = 1}^n \sum\limits_{j = 1}^m \dbinom{n}{i} \dbinom{m}{j} f[i][j][c]\)

时间复杂度 \(O(n^3 m^3 + n^2 m^2 c)\)

代码

#include <cstdio>
using namespace std;

#define int long long

const int maxn = 35;
const int maxm = 35;
const int maxc = 15;
const int sz = 905;
const int mod = 1e9 + 9;

inline int min(const int &a, const int &b) { return (a <= b ? a : b); }
inline int max(const int &a, const int &b) { return (a >= b ? a : b); }

int n, m, c;
int a[maxc];
int fac[sz], inv[sz];
int f[maxn][maxm][maxc], s[maxn][maxm][sz];

int C(int n, int m)
{
    if ((n < 0) || (m < 0) || (n < m)) return 0;
    return fac[n] * inv[m] % mod * inv[n - m] % mod;
}

void init(int lim)
{
    fac[0] = inv[0] = inv[1] = 1;
    for (int i = 1; i <= lim; i++) fac[i] = fac[i - 1] * i % mod;
    for (int i = 2; i <= lim; i++) inv[i] = inv[mod % i] * (mod - mod / i) % mod;
    for (int i = 2; i <= lim; i++) inv[i] = inv[i] * inv[i - 1] % mod;
}

int S(int i, int j, int k)
{
    if ((k < max(i, j)) || (k > i * j)) return 0;
    if (s[i][j][k]) return s[i][j][k];
    for (int p = 0; p <= i; p++)
        for (int q = 0; q <= j; q++)
        {
            int res = C(i, p) * C(j, q) % mod * C((i - p) * (j - q), k) % mod;
            if ((p + q) & 1) s[i][j][k] = (s[i][j][k] - res + mod) % mod;
            else s[i][j][k] = (s[i][j][k] + res) % mod;
        }
    return s[i][j][k];
}

signed main()
{
    scanf("%lld%lld%lld", &n, &m, &c);
    init(n * m);
    for (int i = 1; i <= c; i++) scanf("%lld", &a[i]);
    f[0][0][0] = 1;
    for (int k = 1; k <= c; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                for (int p = 1; p <= i; p++)
                    for (int q = 1; q <= j; q++)
                        f[i][j][k] = (f[i][j][k] + C(i, p) * C(j, q) % mod * f[i - p][j - q][k - 1] % mod * S(p, q, a[k]) % mod) % mod;
    int ans = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            ans = (ans + C(n, i) * C(m, j) % mod * f[i][j][c] % mod) % mod;
    ans = (ans + mod) % mod;
    printf("%lld\n", ans);
    return 0;
}

标签:const,dbinom,limits,int,题解,P3158,棋子,CQOI2011,sum
From: https://www.cnblogs.com/lingspace/p/p3158.html

相关文章

  • HDU 6801 Game on a Circle 题解 (推式子,多项式)
    题目链接首先注意到我们对这个环的扫描是一轮一轮进行的,每轮都会从左到右对每个没被删除的元素以p的概率删除。如果我们能对每个\(t(t\in[0,\infin],t是整数)和i\)求出c......
  • Leetcode面试高频题分类刷题总结及题解(更新中)
    原文链接:Leetcode面试高频题分类刷题总结排序类(Sort)基础知识:快速排序(QuickSort),归并排序(MergeSort)的原理与代码实现。需要能讲明白代码中每一行的目的。快速排序时间......
  • 【题解】P3515 [POI2011]Lightning Conductor(二分栈/分治优化DP)
    [POI2011]LightningConductor题面翻译给定一个长度为\(n\)的序列\(\{a_n\}\),对于每个\(i\in[1,n]\),求出一个最小的非负整数\(p\),使得\(\forallj\in[1,n]\),都......
  • 本博客提供的题解仅供学习,请勿抄袭代码
    近日,发现有部分同学翻取博客上的题解程序复制提交的抄袭行为;在此声明:本博客的代码仅供大家学习参考,对于自身还未学习到对应知识点的同学,请先完善自身基础知识的学习,当且仅......
  • 洛谷P2296 [NOIP2014 提高组] 寻找道路 题解
    题目链接:P2296[NOIP2014提高组]寻找道路-洛谷|计算机科学教育新生态(luogu.com.cn)好了,话不多说上代码:1#include<bits/stdc++.h>2usingnamespacestd;3......
  • Ynoi2019模拟赛题解
    \(Ynoi2019\)模拟赛题解前言:第一次做\(Ynoi\)还是有被离谱到的,我从来没有做到过这么卡常的题目,我到现在\(T1\)都还是\(70\)分,\(lxl\)毒瘤名不虚传啊。但是不得不说,\(Ynoi......
  • 【题解】P1973 [NOI2011] NOI 嘉年华
    yyc学长说是典题,就记一下。题意给出\(n\)个区间,试在丢弃一些区间后,把区间分成两部分,使得不存在同时被两部分中的区间覆盖的位置,求:最终包含区间数较小的部分的区间......
  • 【题解】P1527 [国家集训队]矩阵乘法(整体二分)
    [国家集训队]矩阵乘法题目描述给你一个\(n\timesn\)的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第\(k\)小数。输入格式第一行有两个整数,分别表示矩阵大小\(n......
  • 恶意竞争题解
    恶意竞争题目大意:巨软和微硬两家公司是竞争对手,微硬希望从巨软公司的软件中找到漏洞来打击竞品的销量。巨软公司的软件有\(s\)个子组件,漏洞分为\(n\)类,微硬公司希望在其......
  • Error: Failed to upgrade Homebrew Portable Ruby! 问题解决
    brewconfig==>Downloadinghttps://mirrors.ustc.edu.cn/homebrew-bottles/bottles/bottles-portable-ruby/portable-ruby-2.6.8_1.el_capitan.bottle.tar.gzcurl:(22......