题目链接

题目

题目描述

给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列,要求你在这个矩阵上放N枚棋子(障碍的位置不能放棋子),要求你放N个棋子也满足每行只有一枚棋子,每列只有一枚棋子的限制,求有多少种方案。

输入描述

第一行一个N,接下来一个N*N的矩阵。N ≤ 200,0表示没有障碍,1表示有障碍,输入格式参考样例

输出描述

一个整数,即合法的方案数。

示例1

输入

2
0 1
1 0

输出

1

题解

知识点:排列组合,高精度。

注意到在题目条件下,排列方案数和障碍物出现位置没有任何关系,任何情况都等价于形如下方矩阵的答案:

1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1

我们发现,这个矩阵答案其实就是 \(n\) 个元素的错排数 \(D_n\) 。

众所周知,\(D_n = (n-1)(D_{n-1} + D_{n-2})\) 。小小解释一下:

  1. \((n-1)D_{n-2}\) 表示第 \(n\) 个数和前面 \(n-1\) 个数交换,交换的那个数呆在第 \(n\) 个位置,剩下的错排。
  2. \((n-1)D_{n-1}\) 表示第 \(n\) 个数和前面 \(n-1\) 个数交换,交换的那个数不在第 \(n\) 个位置,和剩下的一起错排。

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; ///继承vector解决位数限制(当前最大位数是9倍整型最大值),操作方便(注意size()返回无符号长整型,尽量不要直接把size放入表达式)
struct Huge_Int :vector<long long> { static const int WIDTH = 9;///压位数,压9位以下 比较安全
static const long long BASE = 1e9;///单位基
static const long long MAX_INT = ~(1 << 31);///最大整型
bool SIGN; ///初始化,同时也可以将低精度转高精度、字符串转高精度
///无需单独写高精度数和低精度数的运算函数,十分方便
Huge_Int(long long n = 0) { *this = n; }
Huge_Int(const string &str) { *this = str; } ///格式化,包括进位和去前导0,用的地方很多,先写一个
Huge_Int &format(int fixlen = 1) {//去0后长度必须大于等于fixlen,给乘法用的
while (size() > fixlen && !back()) pop_back();//去除最高位可能存在的0
if (!back()) SIGN = 0;
for (int i = 1; i < size(); ++i) {
(*this)[i] += (*this)[i - 1] / BASE;
(*this)[i - 1] %= BASE;
}//位内进位
while (back() >= BASE) {
push_back(back() / BASE);
(*this)[size() - 2] %= BASE;
}//位外进位
return *this;//为使用方便,将进位后的自身返回引用
} ///归零
void reset() {
clear();
SIGN = 0;
} ///重载等于,初始化、赋值、输入都用得到
Huge_Int operator=(long long n) {
reset();
SIGN = n < 0;
if (SIGN) n = -n;
push_back(n);
format();
return *this;
}
Huge_Int operator=(const string &str) {
reset();
if (str.empty()) push_back(0);
SIGN = str[0] == '-';
for (int i = str.length() - 1;i >= 0 + SIGN;i -= WIDTH) {
long long tmp = 0;
for (int j = max(i - WIDTH + 1, 0 + SIGN);j <= i;j++)
tmp = (tmp << 3) + (tmp << 1) + (str[j] ^ 48);
push_back(tmp);
}
format();
return *this;
} ///重载输入输出
friend istream &operator>>(istream &is, Huge_Int &tmp) {
string str;
if (!(is >> str)) return is;
tmp = str;
return is;
}
friend ostream &operator<<(ostream &os, const Huge_Int &tmp) {
if (tmp.empty()) os << 0;
else {
if (tmp.SIGN) os << '-';
os << tmp[tmp.size() - 1];
}
for (int i = tmp.size() - 2;i >= 0;i--) {
os << setfill('0') << setw(WIDTH) << tmp[i];
}
return os;
} ///重载逻辑运算符,只需要小于,其他的直接代入即可
///常量引用当参数,避免拷贝更高效
friend bool operator<(const Huge_Int &a, const Huge_Int &b) {
if (a.SIGN ^ b.SIGN) return a.SIGN;
if (a.size() != b.size()) return a.SIGN ? a.size() > b.size() :a.size() < b.size();
for (int i = a.size() - 1; i >= 0; i--)
if (a[i] != b[i])return a.SIGN ? a[i] > b[i] : a[i] < b[i];
return 0;
}
friend bool operator>(const Huge_Int &a, const Huge_Int &b) { return b < a; }
friend bool operator>=(const Huge_Int &a, const Huge_Int &b) { return !(a < b); }
friend bool operator<=(const Huge_Int &a, const Huge_Int &b) { return !(a > b); }
friend bool operator!=(const Huge_Int &a, const Huge_Int &b) { return a < b || b < a; }
friend bool operator==(const Huge_Int &a, const Huge_Int &b) { return !(a != b); } ///重载负号
friend Huge_Int operator-(Huge_Int a) { return a.SIGN = !a.SIGN, a; } ///绝对值函数
friend Huge_Int abs(Huge_Int a) { return a.SIGN ? (-a) : a; } ///加法,先实现+=,这样更简洁高效
friend Huge_Int &operator+=(Huge_Int &a, const Huge_Int &b) {
if (a.SIGN ^ b.SIGN) return a -= (-b);
if (a.size() < b.size()) a.resize(b.size());
for (int i = 0; i < b.size(); i++) a[i] += b[i];//被加数要最大位,并且相加时不要用未定义区间相加
return a.format();
}
friend Huge_Int operator+(Huge_Int a, const Huge_Int &b) { return a += b; }
friend Huge_Int &operator++(Huge_Int &a) { return a += 1; }
friend Huge_Int operator++(Huge_Int &a, int) {
Huge_Int old = a;
++a;
return old;
} ///减法,由于后面有交换,故参数不用引用
friend Huge_Int &operator-=(Huge_Int &a, Huge_Int b) {
if (a.SIGN ^ b.SIGN) return a += (-b);
if (abs(a) < abs(b)) {
Huge_Int t = a;
a = b;
b = t;
a.SIGN = !a.SIGN;
}
for (int i = 0; i < b.size(); a[i] -= b[i], i++) {
if (a[i] < b[i]) {//需要借位
int j = i + 1;
while (!a[j]) j++;
while (j > i) a[j--]--, a[j] += BASE;
}
}
return a.format();
}
friend Huge_Int operator-(Huge_Int a, const Huge_Int &b) { return a -= b; }
friend Huge_Int &operator--(Huge_Int &a) { return a -= 1; }
friend Huge_Int operator--(Huge_Int &a, int) {
Huge_Int old = a;
--a;
return old;
} ///乘法,不能先实现*=,因为是类多项式相乘,每位都需要保留,不能覆盖
friend Huge_Int operator*(const Huge_Int &a, const Huge_Int &b) {
Huge_Int n;
n.SIGN = a.SIGN ^ b.SIGN;
n.assign(a.size() + b.size() - 1, 0);//表示乘积后最少的位数(可能会被format消掉,因此添加了format参数)
for (int i = 0; i < a.size(); i++) {
for (int j = 0; j < b.size(); j++)
n[i + j] += a[i] * b[j];
n.format(n.size());//提前进位
}
return n;//最后进位可能会溢出
}
friend Huge_Int &operator*=(Huge_Int &a, const Huge_Int &b) { return a = a * b; } ///带余除法函数,方便除法和模运算,暂时写不出高效的高精与高精的除法
friend Huge_Int divmod(Huge_Int &a, const Huge_Int &b) {//O(logn),待修改
assert(b != 0);
Huge_Int n;
if (-MAX_INT - 1 <= b && b <= MAX_INT) {//除数小于等于整型才能用这个,不然会溢出
n = a;
n.SIGN = a.SIGN ^ b.SIGN;
long long rest = 0;
long long bl = 0;
for (int i = b.size() - 1;i >= 0;i--) bl = bl * BASE + b[i];
for (int i = n.size() - 1;i >= 0;i--) {
rest *= BASE;
n[i] += rest;
rest = n[i] % bl;
n[i] /= bl;
}
a = a.SIGN ? (-rest) : rest;
return n.format();
}
else {//考虑倍增或者二分优化
n.SIGN = a.SIGN ^ b.SIGN;
for (int i = a.size() - b.size(); abs(a) >= abs(b); i--) {//减法代替除法
Huge_Int c, d;
d.assign(i + 1, 0);
d.back() = 1;
d.SIGN = n.SIGN;
c = b * d;//提高除数位数进行减法
while (abs(a) >= abs(c)) a -= c, n += d;
d.pop_back();
if (!d.empty()) {//遍历压的位
d.back() = BASE / 10;
for (int i = 1;i < WIDTH;i++) {
c = b * d;
while (abs(a) >= abs(c)) a -= c, n += d;
d.back() /= 10;
}
}
}
return n;
}
}
friend Huge_Int operator/(Huge_Int a, const Huge_Int &b) { return divmod(a, b); }
friend Huge_Int &operator/=(Huge_Int &a, const Huge_Int &b) { return a = a / b; }
friend Huge_Int &operator%=(Huge_Int &a, const Huge_Int &b) { return divmod(a, b), a; }
friend Huge_Int operator%(Huge_Int a, const Huge_Int &b) { return a %= b; }
}; Huge_Int f[207];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1;i <= n;i++)
for (int j = 1, x;j <= n;j++)
cin >> x;
f[0] = 1;
f[1] = 0;
for (int i = 2;i <= n;i++) f[i] = (i - 1) * (f[i - 1] + f[i - 2]);
cout << f[n] << '\n';
return 0;
}

NC19999 [HAOI2016]放棋子的更多相关文章

  1. 【BZOJ4563】[Haoi2016]放棋子 错排+高精度

    [BZOJ4563][Haoi2016]放棋子 Description 给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列,要求你在这个矩阵上放N枚棋子(障碍 ...

  2. 洛谷P3182 [HAOI2016]放棋子

    P3182 [HAOI2016]放棋子 题目描述 给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列,要求你在这个矩阵上放N枚棋子(障碍的位置不能放棋子),要 ...

  3. bzoj4563: [Haoi2016]放棋子(错排+高精)

    4563: [Haoi2016]放棋子 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 387  Solved: 247[Submit][Status] ...

  4. [Haoi2016]放棋子 题解

    4563: [Haoi2016]放棋子 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 440  Solved: 285[Submit][Status] ...

  5. BZOJ4563: [Haoi2016]放棋子

    Description 给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列,要求你在 这个矩阵上放N枚棋子(障碍的位置不能放棋子),要求你放N个棋子也满足每行 ...

  6. [HAOI2016] 放棋子及错排问题

    题目 Description 给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列,要求你在这个矩阵上放N枚棋子(障碍的位置不能放棋子),要求你放N个棋子也满足 ...

  7. BZOJ4563:[HAOI2016]放棋子——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4563 给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列 ...

  8. BZOJ 4563: [Haoi2016]放棋子

    Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 389  Solved: 248[Submit][Status][Discuss] Descriptio ...

  9. BZOJ——T 4563: [Haoi2016]放棋子

    Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 387  Solved: 247[Submit][Status][Discuss] Descriptio ...

  10. 洛谷 P3182 [HAOI2016]放棋子(高精度,错排问题)

    传送门 解题思路 不会错排问题的请移步——错排问题 && 洛谷 P1595 信封问题 这一道题其实就是求对于每一行的每一个棋子都放在没有障碍的地方的方案数. 因为障碍是每行.每列只有一 ...

随机推荐

  1. 线性代数 · 矩阵 · Matlab | 满秩分解代码实现

    背景 - 矩阵的满秩分解: 若 A 为 m×n 矩阵,rank(A) = r,则存在 F m×r.G r×n,使得 A = FG. 其中,F 列满秩,G 行满秩. 求满秩分解的方法: 得到 A 的行最 ...

  2. 基于html5开发的Win12网页版,抢先体验

    据 MSPoweruser 报道,Windows 11虽然刚刚开始步入正轨,但最新爆料称微软已经在开启下一个计划,Windows 12 的开发将在 去年3 月份开始.德国科技网站 Deskmodder ...

  3. GoMusic-歌单迁移工具网站 一键迁移网易云/QQ音乐歌单至 Apple/Youtube/Spotify Music

    GoMusic是什么: GoMusic是一个在线歌单迁移工具网站,帮助用户一键迁移网易云/QQ音乐歌单至 Apple/Youtube/Spotify Music,直接输入歌单链接,复制查询结果,打开 ...

  4. 百度网盘(百度云)SVIP超级会员共享账号每日更新(2023.11.30)

    一.百度网盘SVIP超级会员共享账号 可能很多人不懂这个共享账号是什么意思,小编在这里给大家做一下解答. 我们多知道百度网盘很大的用处就是类似U盘,不同的人把文件上传到百度网盘,别人可以直接下载,避免 ...

  5. [转帖]清除掉shared pool中某条sql语句方法

    https://www.xifenfei.com/2012/02/%E6%B8%85%E9%99%A4%E6%8E%89shared-pool%E4%B8%AD%E6%9F%90%E6%9D%A1sq ...

  6. [转帖]GC日志解读,这次别再说看不懂GC日志了

    https://juejin.cn/post/7029130033268555807   测试环境:机器内存16G,JDK 8,12核CPU 测试用例,从网上找的示例,视情况修改即可:   java ...

  7. [转帖]SQL SERVER中什么情况会导致索引查找变成索引扫描

    https://www.cnblogs.com/kerrycode/p/4806236.html SQL Server 中什么情况会导致其执行计划从索引查找(Index Seek)变成索引扫描(Ind ...

  8. [转帖]Python学习之十七_django的入门

    Python学习之十七_django的入门 前言 Python学习了一周, 慢慢总结摸索. 自己还是有多不会的地方. 感慨这些年浪费的时间. 所有的时间都是选择大于努力. 努力最多感动自己. 生活是需 ...

  9. [转帖]MinIO系列7 - Minio性能压测

    https://www.zhihu.com/people/keen-wang 前言 声明:此文为本人历史笔记的整理,文章实际撰写时间为2021年2月份,所以本中所使用的相关组件版本较老.此文是通过压力 ...

  10. 兼容微信支付宝抖音小程序的工具推荐!还能将小程序搬到自己的app里面

    事情的起因是这样的. 之前在微信.支付宝和抖音开放平台都上架了自己的小程序,虽然几个平台有自己的开发标准,但是都是基于 JavaScript 这种已经被广泛使用的编程语言进行开发的,对于开发者而言学习 ...