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; }