UVALive 5971 (LA 5971) Permutation Counting 动态规划 (容斥原理超时)

题目大意:

对于整数 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;
}
优化的版本就不贴了吧..


你可能感兴趣的:(动态规划,uvalive,la,5971,5971)