G.蚌埠住了
题解:
状态划分:
为前个数中,必选第个数组成的长度为k的非严格单调子序列方案数。
状态转移:
当选第个数组成的长度为的序列,它将有前面个长度为的转移而来。即:
,其中。
其世界复杂度为
线段树优化:
我们可以将,优化掉。
即将其放入权值线段树里,查询区间和。复杂度降为
标程:
#include <iostream> #define lch (k<<1) #define rch (k<<1|1) #define mid (l+r>>1) using namespace std; typedef long long ll; const int N=1e4+7; const int S=1e5+1; const ll mod=998244353; int n,s,dp[N][101],tree[400001][101],a[N]; void update(int k,int l,int r,int p,int val,int up){ tree[k][up]=(tree[k][up]+val)%mod; if(l==r) return ; if(p<=mid) update(lch,l,mid,p,val,up); else update(rch,mid+1,r,p,val,up); } int query(int k,int l,int r,int ql,int qr,int up){ if(ql<=l&&r<=qr){ return tree[k][up]; } int sum1=0,sum2=0; if(ql<=mid) sum1=query(lch,l,mid,ql,qr,up); if(mid+1<=qr) sum2=query(rch,mid+1,r,ql,qr,up); return (sum1+sum2)%mod; } int main(){ cin>>n>>s; for(int i=1;i<=n;i++){ cin>>a[i]; } update(1,1,S,1,1,0); for(int i=1;i<=n;i++){ for(int j=1;j<=s;j++){ dp[i][j]=query(1,1,S,1,a[i],j-1)%mod; } for(int j=1;j<=s;j++){ update(1,1,S,a[i],dp[i][j],j); } } ll ans=0; for(int i=1;i<=n;i++){ ans=(ans+dp[i][s])%mod; } cout<<ans<<endl; }