1. 程式人生 > >BZOJ 1925 地精部落(DP)

BZOJ 1925 地精部落(DP)

cnblogs queue pri div close getchar inf class hid

一道很經典的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)