【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 种取值)

  1. 把第 k 个元素也放在第 n 个位置,则有 F[n - 2] 种方案【把剩下的元素错排】

  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(划掉
高精度计算

麦森数

  1. 平时我们估计一个十进制数 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。

    解释一下求位数的公式

    1. log a(b) 的结果 k 表示 a 的 k 次方等于 b
    2. 存在一个换底公式:log a(c) / log b(c) = log b(c)
    3. 显然 log 10(k) 向上取整表示 k 的位数
    4. log 的运算满足规律 log a(b * c) = log a(b) + log a(c)
    5. 由 4 得出,log a(b ^ c) = log a(b) * c

    所以,(log 10(2) * n) + 1 可以表示 2 ^ n 的位数
    (+ 1 的原因很显然啊

  2. 第二问若用高精加法,时间复杂度为 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;  }  
原创粉丝点击