hihoCoder#1743:K-偏差排列(矩阵快速幂+状压dp)

  • 题意:

    如果一个 \(1\to N\) 的排列 \(P=[P_1, P_2, ... P_N]\) 中的任意元素 \(P_i\) 都满足 \(|P_i-i| ≤ K\) ,我们就称 \(P\) 是 \(K\)-偏差排列。

    给定 \(N\) 和 \(K\) ,请你计算一共有少个不同的排列是 \(K\)-偏差排列。

    例如对于 \(N=3\) ,有 \(3\) 个 \(1\)-偏差排列:\([1, 2, 3], [1, 3, 2], [2, 1, 3]\)。

    由于答案可能非常大,你只需要输出答案模 \(1000000007\) 的余数。

    对于 \(70\%\) 的数据,\(1 ≤ N ≤ 1000\)

    对于 \(100\%\) 的数据,\(1 ≤ N ≤ 1000000000, 1 ≤ K ≤ 3\)

  • 题解:

    一道好题~

    这是它的最初版本 #1732 : 1-偏差排列

    那个找规律就是 斐波那契数列 了, dp 的话也是一样的结果 .

    对于这个题我们可以沿用那题思路, 考虑一个位置 \(i\) 能放哪些数, 根据定义能放 \([i-k, i+k]\) 中共 \(2k+1\) 个数.

    考虑状压到 \(i\) 这个点, 这些数中的哪些被放了, 每次转移的时候考虑放入一个数, 这个数之前不能出现, 这样就是合法转移了.

    最后到 \(n\) 的时候, 不能放比 \(n\) 大的数, 且小于等于 \(n\) 的数都要放进去, 只会有那个位置存在正确答案, 这个状态 \(sta=2 ^ {k + 1} - 1\) (也就是意味着 \([n - k, n]\)都得选) .

    当 \(n < k\) 的时候要特判掉一些诡异的特殊情况 .

    然后这样直接写就有 \(70pts\) 了.

    有一些不合法状态不能转移, 也就是要放的数不存在于 \([1, n]\) 之间.

    这样的话, 就是矩阵快速幂套路优化了, 考虑对这个转移系数建立矩阵, 然后它的 \(n\) 次幂中的 \((sta,sta)\) 这个位置就会存在最后的答案咯...

  • 代码:

    70pts:

    #include <bits/stdc++.h>
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    using namespace std;
    
    typedef long long ll;
    
    const ll Mod = 1e9 + 7;
    
    int n, k, all;
    ll ans = 0, dp[2][1500] = {0};
    
    int main () {
        cin >> n >> k;
        if (n <= 2) return printf ("%d\n", n), 0;
    
        all = (1 << (2 * k + 1)) - 1;
    
        dp[0][0] = 1;
    
        int cur = 0;
        For (i, 1, n) {
            For (j, 0, all) if(dp[cur][j]) {
                int sta = (j >> 1);
    
                For (s, 0, 2 * k) if (!(sta & (1 << s))) {
                    int tmp = i - k + s;
                    if (tmp < 1 || tmp > n) continue ;
    
                    (dp[cur ^ 1][sta | (1 << s)] += dp[cur][j]) %= Mod;
                }
    
                dp[cur][j] = 0;
            }
            cur ^= 1;
        }
    
        int Sta = (1 << (k + 1)) - 1;
    
        printf ("%lld\n", dp[cur][Sta]);
        return 0;
    }

    100pts:

    #include <bits/stdc++.h>
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Set(a, v) memset(a, v, sizeof(a))
    using namespace std;
    
    void File() {
    #ifdef zjp_shadow
        freopen ("P1743.in", "r", stdin);
        freopen ("P1743.out", "w", stdout);
    #endif
    }
    
    const int Mod = 1e9 + 7, Maxn = 130;
    
    int n, k, all;
    
    struct Matrix {
        int a[Maxn][Maxn]; Matrix() { Set(a, 0); }
        void Unit() { For (i, 0, all) a[i][i] = 1; }
    };
    
    inline Matrix operator * (Matrix a, Matrix b) {
        Matrix res;
        For (i, 0, all) For (k, 0, all) if (a.a[i][k])
            For (j, 0, all) (res.a[i][j] += 1ll * a.a[i][k] * b.a[k][j] % Mod) %= Mod;
        return res;
    }
    
    inline Matrix fpm(Matrix x, int power) {
        Matrix res; res.Unit();
        for (; power; power >>= 1, x = x * x)
            if (power & 1) res = res * x;
        return res;
    }
    
    Matrix Bas, Ans;
    
    int ans = 0;
    
    int main () {
        File();
    
        cin >> n >> k;
    
        if (n <= 2) return printf ("%d\n", n), 0;
    
        all = (1 << (2 * k + 1)) - 1;
    
        For (i, 0, all) {
            int j = (i >> 1);
            For (s, 0, 2 * k) if (!(j & (1 << s)))
                ++ Bas.a[i][j | (1 << s)];
        }
        Ans = fpm(Bas, n);
    
        int Sta = (1 << (k + 1)) - 1;
    
        printf ("%d\n", Ans.a[Sta][Sta]);
    
        return 0;
    }

原文地址:https://www.cnblogs.com/zjp-shadow/p/9042753.html

时间: 2024-11-09 00:29:27

hihoCoder#1743:K-偏差排列(矩阵快速幂+状压dp)的相关文章

POJ 3744 Scout YYF I 矩阵快速幂优化--概率dp

点击打开链接 Scout YYF I Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 5416   Accepted: 1491 Description YYF is a couragous scout. Now he is on a dangerous mission which is to penetrate into the enemy's base. After overcoming a series diffic

【bzoj1072】【SCOI2007】【排列perm】【状压dp】

Description 给一个数字串s和正整数d, 统计s有多少种不同的排列能被d整除(可以有前导0).例如123434有90种排列能被2整除,其中末位为2的有30种,末位为4的有60种. Input 输入第一行是一个整数T,表示测试数据的个数,以下每行一组s和d,中间用空格隔开.s保证只包含数字0, 1, 2, 3, 4, 5, 6, 7, 8, 9. Output 每个数据仅一行,表示能被d整除的排列的个数. Sample Input 7 000 1 001 1 1234567890 1 1

hdu 5564 Clarke and digits 矩阵快速幂优化数位dp

Clarke and digits Time Limit: 5000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Problem Description Clarke is a patient with multiple personality disorder. One day, Clarke turned into a researcher, did a research on digits. He w

[BZOJ5010][FJOI2017]矩阵填数(状压DP)

5010: [Fjoi2017]矩阵填数 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 90  Solved: 45[Submit][Status][Discuss] Description 给定一个 h*w 的矩阵,矩阵的行编号从上到下依次为 1..h,列编号从左到右依次1..w.在这个矩阵中你需要在每 个格子中填入 1..m 中的某个数.给这个矩阵填数的时候有一些限制,给定 n 个该矩阵的子矩阵,以及该子矩阵的 最大值 v,要求你所填的方案满

HDU1588-Gauss Fibonacci(矩阵快速幂+等比数列二分求和)

题目链接 题意:g(x) = k * x + b.f(x) 为Fibonacci数列.求f(g(x)),从x = 1到n的数字之和sum,并对m取模. 思路: 设A = |(1, 1),(1, 0)| sum = f(b) + f(k + b) + f(2k + b)...+f((n-1)k + b) (f(x) 为Fibonacci数列) sum = A^b + A^(k + b) + A^(2k + b)...+ A^((n-1)k + b) sum = A^b(1 + A^k + A^2k

Matrix Power Series(矩阵快速幂)

C - Matrix Power Series Time Limit:3000MS     Memory Limit:131072KB     64bit IO Format:%I64d & %I64u Submit Status Practice POJ 3233 Description Given a n × n matrix A and a positive integer k, find the sum S = A + A2 + A3 + … + Ak. Input The input

bnu 34895 Elegant String(矩阵快速幂)

题目链接:bnu 34895 Elegant String 题目大意:给定n和k,表示有一个长度为n的序列,序列中的元素由0~k组成,问说有多少个串满足不包含0~k的全排列. 解题思路:矩阵快速幂,根据dp[i][j]表示说第i为有j个相同,写出递推式,根据递推式求出矩阵. #include <cstdio> #include <cstring> typedef long long ll; const ll MOD = 20140518; const int N = 20; str

POJ 3420 Quad Tiling 状压DP+矩阵快速幂

链接:http://poj.org/problem?id=3420 题意:给一个4*N(1 ≤ N ≤ 1e9)的矩形空间,并且给不限块数的1*2的多米诺骨牌,问是由多少种方式能把这个矩形空间填满. 思路:看到这种问题果断想到状压,虽然是在看矩阵的时候看到的这道题.dp[i][j]表示在第i行状态为j的情况下的填满方式数,j的二进制表示中0表示对应位置上一行的骨牌是竖放,或者对应位置的骨牌是横放,1则表示该行该位置的骨牌是竖放.由于N最大1e9所以O(n)的DP绝对超时,用矩阵快速幂来加速DP递

POJ3744——概率DP 矩阵快速幂优化——Scout YYF I

http://poj.org/problem?id=3744 矩阵快速幂: 利用DP的递推式 就本题来说 dp[i] = p*dp[i-1] + (1-p)*dp[i-2] 由于x非常大最大1亿,这样的话复杂度就为1亿 所以这里可以用矩阵的思想 [dp[i]   dp[i-1] ] = [ dp[i-1]  dp[i-2] ] | p   1 - p| | 1      0  | 递推得到 n - 1 [dp[n]   dp[n-1]] = [dp[1]   dp[2] ] |p   1 - p