Codeforces E. Iahub and Permutations 题解存档
E. Iahub and Permutations
题目链接:https://codeforces.com/problemset/problem/340/E
主要是mark一下大佬们的题解和笔记,以方便日后复习
此题有两种做法:
- 容斥原理
原理:http://www.cppblog.com/vici/archive/2011/09/05/155103.aspx
(我反正是不懂) - DP(错位排列)
错位排列的原理:https://www.cnblogs.com/qixingzhi/p/9285830.html
本题题解:https://www.cnblogs.com/qixingzhi/p/9317339.html
个人总结:
- 先理解此状态转移方程的含义:\(f[i]=(i−1)∗(f[i−1]+f[i−2])\)
- 可以根据数字是否为-1,以及数字i是否被使用过,把给的数字a[i]分成4类
My Code
注意一些细节:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2005, mod = 1e9 + 7;
int n, x, y;
int a[N], f[N];
bool vis[N]; //有无被使用过
//求阶乘
int mul (int x) {
int res = 1;
for (int i = 2; i <= x; i ++)
res = (res * i) % mod;
return res;
}
signed main () {
cin >> n;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
if (a[i] != -1)
vis[a[i]] = true;
}
for (int i = 1; i <= n; i ++) {
if (a[i] == -1 && vis[i])
x ++;
if (a[i] == -1 && !vis[i]) //a[i]!=-1的时候你也不需要操作啊!!
y ++;
}
f[0] = mul (x);
for (int i = 1; i <= y; i ++) {
f[i] = ((x + i - 1) * f[i - 1]) % mod; //算上前面那一段
if (i > 1) //避免i-2为负数
f[i] = (f[i] + (i - 1) * f[i -2]) % mod;
}
cout << f[y] << endl;
}
//情况:
//1. a[i]=-1,i未被使用过——y
//2. a[i]=-1,i已被使用过——x
//3. a[i]!=-1,i未被使用过——y
//4. a[i]!=-1,i已被使用过——0