首页 > 代码库 > BZOJ 1925 地精部落(DP)

BZOJ 1925 地精部落(DP)

一道很经典的DP题。

题意:求n排列中波动排列的种数。

不妨考虑DP,令dp1[i][j],表示1-j的排列中,第一项为i之后递增的波动排列种数。dp2[i][j]表示1-j的排列中,第一项为i之后递减的波动排列种数。

显然有一个性质,dp1[i][j]=dp2[j+1-i][j],将各项用j+1减去即可。

所以我们主要观察dp1数组。

如果第一项放了i,之后的数字是1,2,,,i-1,i+1,i+2,,j。

如果我们把大于i的数减去1,就又变成了j-1的一个排列,那么则有dp1[i][j]=sum(dp2[i][j-1]+dp2[i+1][j-1]+...dp2[j-1][j-1]).

因为dp2[i][j]=dp1[j+1-i][j],所以dp1[i][j]=sum(dp1[1][j-1]+dp1[2][j-1]+....+dp1[j-i][j-1]).

可以用前缀和优化转移。用滚动数组优化空间。时间复杂度O(n^2).

 

技术分享
# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <bitset>
# include <set>
# include <cmath>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi acos(-1.0)
# define eps 1e-8
# define MOD 1000000007
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FO(i,a,n) for(int i=a; i<n; ++i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
int Scan() {
    int x=0,f=1;char ch=getchar();
    while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();}
    return x*f;
}
const int N=4205;
//Code begin...

int dp[2][N], sum[N];

int main ()
{
    int n, P, flag=0;
    scanf("%d%d",&n,&P);
    dp[0][1]=1; sum[1]=1;
    for (int i=n-1; i>=1; --i) {
        flag^=1; mem(dp[flag],0);
        FOR(j,1,n-i+1) if (n-i-j>=0) dp[flag][j]=sum[n-i-j+1];
        mem(sum,0);
        FOR(j,1,n-i+1) sum[j]=(sum[j-1]+dp[flag][j])%P;
    }
    LL ans=0;
    FOR(i,1,n) ans=(ans+dp[flag][i])%P;
    ans=ans*2%P;
    printf("%lld\n",ans);
    return 0;
}
View Code

 

BZOJ 1925 地精部落(DP)