[HAOI2016]放棋子

题意

Here

思考

看第一眼:状压dp,再看范围gg

第二眼:普通dp,貌似可以直接递推?

其实就是个很裸的错排问题,写个博客顺便复习下~

错排问题就是说一个 \(n\) 的排列,每个元素都满足 \(a[i] != i\),求方案数
\(f[n]\)\(n\) 的错排方案数,我们可以考虑递推:

  1. 放第 \(n\) 个元素,有 \(n-1\) 种方法
  2. 假设第 \(n\) 个元素放在了 \(p\) 位置,那么放编号为 \(p\) 的元素:

    1. 放在 \(n\) 位置,对于剩下 \(n-2\) 个元素,就有 \(f[n-2]\) 种放法
    2. 不放在 \(n\) 位置,对于剩下 \(n-1\) 个元素,就有 \(f[n-1]\) 种放法

那么递推方程:
\[f[n] = (n-1)*(f[n-1]+f[n-2])\]

再说说这题为什么是错排问题,首先,我们发现每一个障碍具体在哪不会影响答案,行与行的互换也是不影响的,我们可以将每个棋盘都转变为一个 \(n*n\) 的对角线上不能放棋子的棋盘,所以这就是一个错排问题了~

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct BIG{
    int S[10010], len;
    void givint(int x){
        memset(S, 0, sizeof(S)); len = 0;
        while(x){
            S[++len] = x % 10; x /= 10;
        }
    }
    void clearx(){ memset(S, 0, sizeof(S)); len = 0; }
    BIG operator + (const BIG &a) const{
        BIG ans; ans.clearx();
        for(int i=1; i<=10000; i++) ans.S[i] += a.S[i] + S[i];
        for(int i=1; i<=10000; i++) ans.S[i+1] += ans.S[i]/10, ans.S[i]%=10;
        ans.len = 10000; while(!ans.S[ans.len]) ans.len --;
        return ans;
    }
    BIG operator * (const BIG &a) const{
        BIG ans; ans.clearx();
        for(int i=1; i<=a.len; i++)
            for(int j=1; j<=len; j++) ans.S[i+j-1] += a.S[i] * S[j];
        for(int i=1; i<=10000; i++) ans.S[i+1] += ans.S[i]/10, ans.S[i]%=10;
        ans.len = 10000; while(!ans.S[ans.len]) ans.len --;
        return ans;
    }
    void print(){
        for(int i=len; i>=1; i--) cout<<S[i];
    }
}D[220];
int n;
int main(){
    D[1].givint(0), D[2].givint(1);
    cin >> n;
    for(int i=3; i<=n; i++){
        BIG tmp; tmp.givint(i - 1);
        D[i] = BIG(tmp * (D[i-1] + D[i-2]));
    }
    D[n].print();
    return 0;
}

总结

看出错排问题这题就很水了。(\(ps:\) 注意写高精度)

猜你喜欢

转载自www.cnblogs.com/alecli/p/9912354.html