UOJ #311「UNR #2」积劳成疾

需要锻炼$ DP$能力

UOJ #311


题意

等概率产生一个长度为$ n$且每个数在[1,n]间随机的数列

定义其价值为所有长度为$ k$的连续子数列的最大值的乘积

给定$ n,k$求所有合法数列的价值和


题解

设$ f(x,y)$表示长度为$x$的数列中,最值不超过$ y$的所有数列的价值和

若数列的最值不是$ y$则$ f(x,y)=f(x,y-1)$

否则枚举最左边的最值位置,设为位置$ i$

则$ f(x,y)$可由$f(i-1,y-1)·w(y)^{calc(i)}·f(x-i,y)$转移过来

其中$ calc(i)$表示在长度为$ x$的数列中有多少个长度为$ k$的数列包含第$ i$个位置

时间复杂度$ O(n^3)$


代码

#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#define p 998244353
#define rt register int
#define ll long long
using namespace std;
inline ll read(){
    ll x=0;char zf=1;char ch=getchar();
    while(ch!='-'&&!isdigit(ch))ch=getchar();
    if(ch=='-')zf=-1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf;
}
void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
void writeln(const ll y){write(y);putchar('\n');}
int k,m,n,x,y,z,cnt,ans;
int w[405];
int calc(int x,int y){
    int L=max(1,x-m+1),R=min(x,y-m+1);
    return max(0,R-L+1);
}
int f[405][405],mi[405][405];
int main(){
    n=read();m=read();
    for(rt i=1;i<=n;i++)w[i]=read();
    for(rt i=0;i<=n;i++)f[0][i]=1;
    for(rt i=1;i<=n;i++){
        mi[i][0]=1;
        for(rt j=1;j<=n;j++)mi[i][j]=1ll*mi[i][j-1]*w[i]%p;
    }
    for(rt i=1;i<=n;i++)
    for(rt j=1;j<=n;j++){
        if(j>1)f[i][j]=f[i][j-1];
        for(rt k=1;k<=i;k++)(f[i][j]+=1ll*f[k-1][j-1]*f[i-k][j]%p*mi[j][calc(k,i)]%p)%=p;
    }
    cout<<f[n][n];
    return 0;
}

 

posted @ 2019-01-10 13:06  Kananix  阅读(213)  评论(0编辑  收藏  举报

Contact with me