题目大意:
对于整数 1 到 n 进行排列, 如果最终的排列结果没有出现 i + 1 正好在 i 的后一位的情况,就成这个排列是 good 的, 问对于整数 1 到 n 有多少种不同的排列方式是 good 的排列
大致思路:
这道题一眼看过去就以为是容斥原理, 后来发现 n 高达 10 ^ 6 , 于是之前写的容斥原理理所当然 TLE 了...
后来想了一些优化还是TLE了..没注意到有 10^4组Case...
后来想了一下只能是dp, 于是才有了接下来的dp递推的式子:
用 dp[n] 表示 1 到 n 的good排列数
那么对于dp[n + 1] 我们先考虑将 1 到 n 线排列好, 将 n + 1插入之后是一个 good 的排列, 那么如果 1 到 n 之前已经就是一个good 的排列, 再加入 n + 1时, n + 1 可以插入在n 个空位中(不能放在原来good 地排列的 n 的后面) 这样有 n*dp[n] 中
另外一种可能就是原本1 到n 的排列当中就存在着唯一一对相邻的 i 和 i + 1,我们将 n + 1插入在这两个数中间打破相邻状态成为good 排列, 这样对于一个存在唯一一对相邻的排列只能有1个位置选择, 这样的排列的个数为C(1, n - 1)*dp[n - 1], 因为i 和 i + 1有C(1, n - 1)种选择, 而每种选择相邻的之后将相邻的看做一个整体, 另外的都不能相邻, 相当于n - 1个数的good 排列数, 故有 ( n - 1)*dp[n - 1]种可能
这样便得到了递推式:
dp[n + 1] = n*dp[n] +(n - 1)*dp[n - 1]
其中dp[1] = 1, dp[2] = 1;
先预处理求出所有的 dp[k]之后查询输出时间复杂度为 O(N + T)
代码如下:
Result : Accepted Memory : 0 KB Time : 49 ms
/* * Author: Gatevin * Created Time: 2014/8/3 13:48:36 * File Name: test.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; lint dp[1000100]; const lint mod = 1000000007LL; int main() { int t; int n; dp[1] = 1LL; dp[2] = 1LL; dp[3] = 3LL; for(int i = 4; i <= 1000000; i++) { dp[i] = (((i - 1) * dp[ i - 1]) % mod + (( i - 2) * dp[ i - 2]) % mod) % mod; } cin>>t; for(int cas = 1; cas <= t; cas++) { cin>>n; cout<<"Case "<<cas<<": "<<dp[n]<<endl; } return 0; }
思想: Ai = { 数字 i 和 i + 1相连) , 容斥很简单但是这题的数据范围显然会超时
Result : Time Limit Exceeded
/* * Author: Gatevin * Created Time: 2014/8/3 12:40:25 * File Name: test.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; const lint mod = 1000000007LL; lint A[1000100]; int n; lint quick_pow(lint base, lint pow) { lint ret = 1; while(pow) { if(pow&1) { ret = (ret*base) % mod; } base = (base * base) % mod; pow >>= 1; } return ret; } lint getc(lint a, lint b) { if(a < b) return 0; if(b > a - b) b = a - b; lint s1 = 1; lint s2 = 1; for(int i = 0; i < b; i++) { s1 = (s1*(a - i)) % mod; s2 = (s2*(i + 1)) % mod; } return s1*quick_pow(s2, mod - 2) % mod; } lint lucas(lint a, lint b) { if(b == 0) return 1; return getc(a % mod, b % mod) * lucas(a / mod, b / mod) % mod; } lint solve() { lint answer = 0; int sign = 1; for(int i = 0; i <= n - 1; i++) { if(i & 1) { sign = -1; } else { sign = 1; } lint tmp = lucas(n - 1, i); answer = (answer + (sign*(tmp*A[n - i]) % mod + mod) % mod) % mod;; } return answer; } int main() { int t; A[1] = 1; A[0] = 1; for(int i = 2; i <= 1000000; i++) { A[i] = (A[i - 1] * i) % mod; } cin>>t; for(int cas = 1; cas <= t; cas++) { cin>>n; cout<<"Case "<<cas<<": "<<solve()<<endl; } return 0; }优化的版本就不贴了吧..