1. 程式人生 > >UOJ #311「UNR #2」積勞成疾

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