NC19999 [HAOI2016]放棋子
题目
题目描述
给你一个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})\) 。小小解释一下:
- \((n-1)D_{n-2}\) 表示第 \(n\) 个数和前面 \(n-1\) 个数交换,交换的那个数呆在第 \(n\) 个位置,剩下的错排。
- \((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]放棋子的更多相关文章
- 【BZOJ4563】[Haoi2016]放棋子 错排+高精度
[BZOJ4563][Haoi2016]放棋子 Description 给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列,要求你在这个矩阵上放N枚棋子(障碍 ...
- 洛谷P3182 [HAOI2016]放棋子
P3182 [HAOI2016]放棋子 题目描述 给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列,要求你在这个矩阵上放N枚棋子(障碍的位置不能放棋子),要 ...
- bzoj4563: [Haoi2016]放棋子(错排+高精)
4563: [Haoi2016]放棋子 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 387 Solved: 247[Submit][Status] ...
- [Haoi2016]放棋子 题解
4563: [Haoi2016]放棋子 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 440 Solved: 285[Submit][Status] ...
- BZOJ4563: [Haoi2016]放棋子
Description 给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列,要求你在 这个矩阵上放N枚棋子(障碍的位置不能放棋子),要求你放N个棋子也满足每行 ...
- [HAOI2016] 放棋子及错排问题
题目 Description 给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列,要求你在这个矩阵上放N枚棋子(障碍的位置不能放棋子),要求你放N个棋子也满足 ...
- BZOJ4563:[HAOI2016]放棋子——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4563 给你一个N*N的矩阵,每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列 ...
- BZOJ 4563: [Haoi2016]放棋子
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 389 Solved: 248[Submit][Status][Discuss] Descriptio ...
- BZOJ——T 4563: [Haoi2016]放棋子
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 387 Solved: 247[Submit][Status][Discuss] Descriptio ...
- 洛谷 P3182 [HAOI2016]放棋子(高精度,错排问题)
传送门 解题思路 不会错排问题的请移步——错排问题 && 洛谷 P1595 信封问题 这一道题其实就是求对于每一行的每一个棋子都放在没有障碍的地方的方案数. 因为障碍是每行.每列只有一 ...
随机推荐
- Oracle实例的启动和关闭
启动模式 1.NoMount 模式(启动实例不加载数据库) 命令:startup nomount 讲解:这种启动模式只会创建实例,并不加载数据库,Oracle仅为实例创建各种内存结构和服务进程,不会打 ...
- NSSCTF Round#11 Basic 密码个人赛复盘
[NSSRound#11 Basic]ez_enc ABAABBBAABABAABBABABAABBABAAAABBABABABAAABAAABBAABBBBABBABBABBABABABAABBAA ...
- [转帖]nginx源码层面探究request_time、upstream_response_time、upstream_connect_time与upstream_header_time指标具体含义与区别
https://www.cnblogs.com/AcAc-t/p/nginx_request_time_upstream_respone_time_analysis.html 背景概述 最近计划着重分 ...
- [转帖]Shell if 条件判断
Shell 语言中的if条件 一.if的基本语法: if [ command ];then 符合该条件执行的语句 elif [ command ];then 符合该条件执行的语句 e ...
- 【转帖】nginx变量使用方法详解-1
https://www.diewufeiyang.com/post/575.html Nginx 的配置文件使用的就是一门微型的编程语言,许多真实世界里的 Nginx 配置文件其实就是一个一个的小程序 ...
- [转帖]新一代垃圾回收器ZGC的探索与实践
1. 引入 1.1 GC之痛 很多低延迟高可用Java服务的系统可用性经常受GC停顿的困扰. GC停顿指垃圾回收期间STW(Stop The World),当STW时,所有应用线程停止活动,等待GC停 ...
- 简单定位占用最高CPU的java进程信息
公司里面一个应用不小心点击就会导致系统性能下降很明显. 性能组的同事定位到了, 我这里以学习的态度重现一下这个过程. 1. 问题再现 产品一个非常大数据量的帮助, 点击之后就会占用非常多的cpu 因为 ...
- React中Props的详细使用和props的校验
props中的children属性 组件标签只用有子节点的时候,props就会有该属性; children的属性跟props一样的,值可以是任意值;(文本,React元素,组件,函数) 组件: < ...
- openAI发布v0.2.0了
时隔20天,OpenAI从v0.0.1升级到了v0.2.0.与v0.0.1版相比,v0.2.0版主要做了以下改动: 把cmd目录下微信公众号的相关服务迁移到了这里 完善了cmd下的测试服务,针对ope ...
- 从零开始配置 vim(15)——状态栏配置
vim 下侧有一个状态栏,会显示当前打开的文件等一系列内容,只是我们很少去关注它.而且原生的vim也支持对状态栏进行自定义.这篇文章主要介绍如何自定义状态栏 设置状态栏 我们可以采用 set stat ...