动态规划训练之六

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;
}
posted @ 2019-10-08 21:51  wzx_believer  阅读(115)  评论(0编辑  收藏  举报