[HAOI2016]放棋子
Posted alecli
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[HAOI2016]放棋子相关的知识,希望对你有一定的参考价值。
题意
思考
看第一眼:状压dp,再看范围gg
第二眼:普通dp,貌似可以直接递推?
其实就是个很裸的错排问题,写个博客顺便复习下~
错排问题就是说一个 (n) 的排列,每个元素都满足 (a[i] != i),求方案数
记 (f[n]) 为 (n) 的错排方案数,我们可以考虑递推:
- 放第 (n) 个元素,有 (n-1) 种方法
假设第 (n) 个元素放在了 (p) 位置,那么放编号为 (p) 的元素:
- 放在 (n) 位置,对于剩下 (n-2) 个元素,就有 (f[n-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:) 注意写高精度)
以上是关于[HAOI2016]放棋子的主要内容,如果未能解决你的问题,请参考以下文章