【BZOJ 4563 放棋子】【错排】
来源:互联网 发布:neflix 淘宝账号 编辑:程序博客网 时间:2024/06/06 19:21
BZOJ 4563 放棋子
每行每列有一个障碍,所以我们发现任意交换两行或者两列对答案没有影响,不妨把障碍交换到主对角线位置,就变成了错排问题。
用 F[n] 来表示 n 个元素的错排方案,F[n] = (n - 1) * (F[n - 1] + F[n - 2])
理解一下:
如果我们把第 n 个元素放在第 k 个位置( k 有 n - 1 种取值)
把第 k 个元素也放在第 n 个位置,则有 F[n - 2] 种方案【把剩下的元素错排】
把第 k 个元素放在其他位置,则有 F[n - 1] 种方案【也可以看做把 1 ~ n - 1 的元素错排,然后交换 n 和 k】
另外错排公式是:
F[n] = (n !)[(-1) ^ 0 / 0 ! +(-1) ^ 1 / (1 !) + (-1) ^ 2 / (2 !) + (-1) ^ 3 / (3 !) + …… + (- 1) ^ n / (n !)]
特别地有 0 ! = 1,1 != 1
需要高精
(赶紧顺便复习一下高精 qwq(划掉
高精度计算
麦森数
平时我们估计一个十进制数 x (x > 0)的位数,往往潜在地使用了放缩法:
若 10 ^ k <= x < 10 ^ (k + 1),则 x 有 k + 1 位。而当 x = 2 ^ p - 1 时,由于 x 的末位不为 0,因此不存在借位现象,所以 x 的位数等于 2 ^ p 的位数。
又假设 10 ^ k <= 2 ^ p < 10 ^ (k + 1),此中的 k,便等于 [log 10(2 ^ p)] 也就是 int(log 10(2 ^ p))。
最后变换一下,第一问的答案便是 int(p * log 10(2)) + 1。
解释一下求位数的公式
- log a(b) 的结果 k 表示 a 的 k 次方等于 b
- 存在一个换底公式:log a(c) / log b(c) = log b(c)
- 显然 log 10(k) 向上取整表示 k 的位数
- log 的运算满足规律 log a(b * c) = log a(b) + log a(c)
- 由 4 得出,log a(b ^ c) = log a(b) * c
所以,(log 10(2) * n) + 1 可以表示 2 ^ n 的位数
(+ 1 的原因很显然啊第二问若用高精加法,时间复杂度为 O(n)。但常数过大,超时在所难免。
于是想到,要用快速幂高精。
只有一点不同,此处的快速幂是递归形式的。
当由递归得到 2 ^ (p / 2) 的值时,便可以通过 2 ^ p = 2 ^ (p / 2) * 2 ^ (p / 2) 得到 2 ^ p 的值,若 p 是奇数,2 ^ p = 2 ^ (p / 2) * 2 ^ (p / 2) * 2 即可。
以下是递归的框架:
void work(long long n) { // n 即为 p 值 if(n == 0) return ; work(n / 2); if(n % 2 == 0) 根据 2 ^ p = 2 ^ (p / 2) * 2 ^ (p / 2),由高精得到 a = b * b; if(n % 2 == 1) 根据 2 ^ p = 2 ^ (p / 2) * 2 ^ (p / 2) * 2,由高精得到 a = b * b * 2; 处理 a 数组进位,更新 b 数组; 清空 a 数组;}
代码:
#include <bits/stdc++.h>using namespace std;int a[10005], b[10005];void work(int n) { if(n == 0) return ; work(n / 2); if(n % 2 == 0) { for(int i = 0; i < 500; i ++) for(int j = 0; j < 500; j ++) a[i + j] += b[i] * b[j]; } else if(n % 2) { for(int i = 0; i < 500; i ++) for(int j = 0; j < 500; j ++) a[i + j] += b[i] * b[j] * 2; } for(int i = 0 ; i < 500; i ++) { b[i] = a[i] % 10; a[i + 1] += a[i] / 10; } memset(a, 0, sizeof(a));}int main() { int p; scanf("%d", &p); printf("%d\n", (int)(log10(2) * p) + 1); b[0] = 1; work(p); for(int i = 499; i > 0; i --) { printf("%d", b[i]); if((i + 1) % 50 == 1) printf("\n"); } printf("%d\n", b[0] - 1); return 0;}
本题代码:
#include <cstring>#include <algorithm>#include <iostream>#include <cstdio>using namespace std;struct use { int a[1005]; // a[0] 代表 f 的长度}f[205];int n;use operator + (use a, use b) { use c; int t = 0; c.a[0] = max(a.a[0], b.a[0]); for(int i = 1; i <= c.a[0]; i ++) { c.a[i] = a.a[i] + b.a[i] + t; if(c.a[i] >= 10) { t = c.a[i] / 10; c.a[i] %= 10; } else t = 0; } if(t > 0) c.a[++ c.a[0]] = t; return c;}use operator * (int x, use a) { // 高精度 * 单精度 int t = 0; for(int i = 1; i <= a.a[0]; i ++) a.a[i] *= x; for(int i = 1; i <= a.a[0]; i ++) { a.a[i] += t; if(a.a[i] >= 10) { t = a.a[i] / 10; a.a[i] %= 10; } else t = 0; } while(t) { a.a[++ a.a[0]] = t % 10; t /= 10; } return a; }int main() { scanf("%d", &n); f[1].a[1] = 0; f[2].a[1] = f[1].a[0] = f[2].a[0] = 1; for(int i = 3; i <= n; i ++) f[i] = (i - 1) * (f[i - 1] + f[i - 2]); for(int i = f[n]. a[0]; i >= 1 ; i --) printf("%d", f[n].a[i]); return 0; }
- 【BZOJ 4563 放棋子】【错排】
- BZOJ-4563 [HAOI-2016] 放棋子 错排问题+高精度
- BZOJ 4563 [Haoi2016]放棋子 错排公式+高精度
- BZOJ 4563: [Haoi2016]放棋子
- BZOJ 4563: [Haoi2016]放棋子
- bzoj 4563: [Haoi2016]放棋子 动态规划
- [BZOJ4563][Haoi2016]放棋子 错排公式+高精度
- 【BZOJ 3294】[Cqoi2011]放棋子
- BZOJ 3294: [Cqoi2011]放棋子
- BZOJ 3294: [Cqoi2011]放棋子
- BZOJ P3294[Cqoi2011]放棋子
- 【BZOJ】4563 [Haoi2016]放棋子 递推+高精
- [BZOJ]4563: [Haoi2016]放棋子 容斥+高精度
- 4563: [Haoi2016]放棋子
- 【组合计数】【bzoj 3294】: [Cqoi2011]放棋子
- 放棋子
- 放棋子
- 放棋子
- 运维之高级服务篇------1. 运维概述 、 Squid代理服务器 、 Varnish服务器
- 在Centos6.6下编译安装最新apache
- K-近邻算法(kNN)
- 看图说话排序算法之快速排序
- 【笔记】《WebGL编程指南》学习-第6章着色器语言(2-矢量和矩阵)
- 【BZOJ 4563 放棋子】【错排】
- STL之函数适配器
- redis获取异常或者获取不到
- 1020. Rope
- spring源代码-DispatcherServlet
- Linux 输出重定向>和>>的区别是什么?
- 设计模式
- 对java中多态的理解
- oracle数据库连接出现的错误