动态规划训练之六

时光毁灭记忆、已成空白 提交于 2019-11-30 19:49:55

https://www.luogu.org/problem/P2467

这是一道好题

题目描述

求1-n排列组成的波动数列的个数

分析首先肯定是个dp没错了,考虑设计方案,

dp[i,j],表示用1-i的排列最后一个为j的方案数

dp[i,j]相当于dp[i-1,k]中原排列大于等于j的数都加1,再把j插到末尾后的新合法排列的方案数

类似test10.7的排列题

答案有“M"型与"W"型,显然方案数是一样的,这里只考虑"W"型的,最后把答案*2就行了

这时你可能会有疑问,为什么偶数是枚举[1,j-1],而奇数是枚举[j,i-1],

因为只考虑“W”形态的,所以奇数一定是山峰的,而偶数一定山谷

所以奇数枚举的一定要比前一个位置上的数大,偶数枚举的一定要比前一个位置上的数小

#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define N 4211 #define M(a) ((a)<=mod?(a):(a-mod)) inline int read(){     int x=0,f=1;     char c=getchar();     while(c<'0'||c>'9'){         if(c=='-')f=-1;         c=getchar();     }     while(c>='0'&&c<='9'){         x=(x<<3)+(x<<1)+c-'0';         c=getchar();     }     return x*f; } int n,mod; int dp[N][N],ans=0; int main(){     n=read(),mod=read();     for(int i=1;i<=n;i++){         dp[1][i]=1;     }     for(int i=2;i<=n;i++){         for(int j=1;j<=n;j++){             if(i&1){                 for(int k=j;k<i;k++){                     dp[i][j]=M(dp[i][j]+dp[i-1][k]);                  }             }             else{                 for(int k=1;k<j;k++){                     dp[i][j]=M(dp[i][j]+dp[i-1][k]);                 }             }         }     }     for(int i=1;i<=n;i++){         ans=M(ans+dp[n][i]);     }      cout<<2*ans%mod<<endl;     return 0; }

下面的代码是用树状数组维护的,实际上可以用前缀和就好(代码不是我写的,不然我肯定前缀和)
还有就是要开O2才能过
code by std:

#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define N 4211 #define M(a) ((a)<=mod?(a):(a-mod)) inline int read(){     int x=0,f=1;     char c=getchar();     while(c<'0'||c>'9'){         if(c=='-')f=-1;         c=getchar();     }     while(c>='0'&&c<='9'){         x=(x<<3)+(x<<1)+c-'0';         c=getchar();     }     return x*f; } int n,mod; int dp[N][N],ans=0; int b[N]; int lowbit(int x){     return x&(-x); } void Add(int x,int d){     while(x<=n){         b[x]=M(b[x]+d);         x+=lowbit(x);     } } int Ask(int x){     int ans=0;     while(x){         ans=M(ans+b[x]);         x-=lowbit(x);     }     return ans; } int main(){     n=read(),mod=read();     for(int i=1;i<=n;i++){         dp[1][i]=1;         Add(i,1);     }     for(int i=2;i<=n;i++){         for(int j=1;j<=n;j++){             if(i&1){                 if(i>j){                     dp[i][j]=M(Ask(i-1)-Ask(j-1)+mod);                 }             }             else{                 dp[i][j]=Ask(j-1);             }         }         memset(b,0,sizeof(b));         for(int j=1;j<=n;j++){             Add(j,dp[i][j]);         }     }     for(int i=1;i<=n;i++){         ans=M(ans+dp[n][i]);     }      cout<<2*ans%mod<<endl;     return 0; }
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!