Codeforces 1474F. 1 2 3 4 ... 題解
阿新 • • 發佈:2021-01-21
題目大意:給定一個序列,求其中最長嚴格上升子序列長度及其個數。
序列按如下方式給出:給定 \(n(1\leq n\leq 50)\) 和序列中的第一個數 \(x(-10^9\leq x\leq 10^9)\),接下來 \(n\) 個數 \(d_i(-10^9\leq d_i\leq 10^9)\),若 \(d_i>0\),則重複 \(d_i\) 次,在序列末尾加上當前序列最後一個數 \(+1\) 的值,若 \(d_i<0\),重複 \(-d_i\) 次,在序列末尾加上當前序列最後一個數 \(-1\) 的值。
題解:首先 \(x\) 沒有任何用處,證明顯然。
首先求最長上升子序列長度非常容易,直接 \(O(n^2)\)
接下來的主要問題是如何求出最長上升子序列的方案數,容易發現如果構成最長上升子序列的最大值和最小值不唯一,那麼這些最大值和最小值所在的區間一定不交,所以可以直接拆開來算,所以我們只需要考慮只有一種最小值和最大值的情況。
假設序列開頭是最小值(這容易做到),那麼我們就可以考慮 DP 了,設 \(f_{v,i}\) 表示當前的值是 \(v\),當前所在的段是 \(i\)(段就是一整段上升或下降)的方案數,這個轉移十分顯然,並且容易發現在很大的一段區間內轉移都是相同的,可以採用矩陣快速冪來優化。
不過還是需要特判一下 \(d_i\) 全部都是負數的情況。
時間複雜度:\(O(n^3\log A)\)(其中 \(A\) 是值域)。
程式碼:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int Maxn=50; const int Mod=998244353; int n; int a[Maxn+5],a_len; int m; struct Matrix{ int a[Maxn+5][Maxn+5]; void init_zero(){ for(int i=0;i<=m;i++){ for(int j=0;j<=m;j++){ a[i][j]=0; } } } void init(){ for(int i=0;i<=m;i++){ for(int j=0;j<=m;j++){ a[i][j]=(i==j); } } } friend Matrix operator *(Matrix a,Matrix b){ Matrix ans; ans.init_zero(); for(int i=0;i<=m;i++){ for(int k=0;k<=m;k++){ for(int j=0;j<=m;j++){ ans.a[i][j]=(ans.a[i][j]+1ll*a.a[i][k]*b.a[k][j])%Mod; } } } return ans; } }; Matrix quick_power(Matrix a,int b){ Matrix ans; ans.init(); while(b){ if(b&1){ ans=ans*a; } b>>=1; a=a*a; } return ans; } struct Vector{ int a[Maxn+5]; void init_zero(){ for(int i=0;i<=m;i++){ a[i]=0; } } friend Vector operator *(Vector a,Matrix b){ Vector ans; ans.init_zero(); for(int i=0;i<=m;i++){ for(int j=0;j<=m;j++){ ans.a[j]=(ans.a[j]+1ll*a.a[i]*b.a[i][j])%Mod; } } return ans; } }; struct Segment{ ll l,r; }seg[Maxn+5]; int seg_len; ll d[Maxn*6+5]; int d_len; int solve(int l,int r){ m=r-l+1; seg_len=0; ll x=0; for(int i=l;i<=r;i++){ seg_len++; seg[seg_len].l=(x+(a[i]<0?-1:1)); seg[seg_len].r=x+a[i]; x+=a[i]; } seg[1].l=0; d_len=0; for(int i=1;i<=seg_len;i++){ d[++d_len]=seg[i].l-1; d[++d_len]=seg[i].l; d[++d_len]=seg[i].l+1; d[++d_len]=seg[i].r-1; d[++d_len]=seg[i].r; d[++d_len]=seg[i].r+1; } sort(d+1,d+1+d_len); d_len=unique(d+1,d+1+d_len)-d-1; d_len--; Vector ans; ans.init_zero(); ans.a[0]=1; ll lst=-1; for(int i=1;i<=d_len;i++){ Matrix tmp; tmp.init_zero(); for(int j=1;j<=seg_len;j++){ if(min(seg[j].l,seg[j].r)<=lst+1&&max(seg[j].l,seg[j].r)>=d[i]){ if(seg[j].l<seg[j].r){ for(int k=0;k<=j;k++){ tmp.a[k][j]=1; } } else{ for(int k=0;k<j;k++){ tmp.a[k][j]=1; } } } } tmp=quick_power(tmp,d[i]-lst); ans=ans*tmp; lst=d[i]; } int sum=0; for(int i=0;i<=m;i++){ sum=(sum+ans.a[i])%Mod; } return sum; } int main(){ scanf("%d%*d",&n); for(int i=1;i<=n;i++){ int x; scanf("%d",&x); if(x!=0){ a[++a_len]=x; } } n=a_len; ll ans=0; ll sum=0; for(int i=1;i<=n;i++){ ll tmp=sum; for(int j=i;j<=n;j++){ tmp+=a[j]; ans=max(ans,tmp-sum); } sum+=a[i]; } if(ans==0){ int val=1; for(int i=1;i<=n;i++){ val=(val-a[i])%Mod; } printf("%lld %d\n",ans+1,val); return 0; } sum=0; int val=0; for(int i=1;i<=n;i++){ ll tmp=sum; int right=-1; for(int j=i;j<=n;j++){ tmp+=a[j]; if(tmp-sum==ans){ right=j; } } sum+=a[i]; if(right!=-1){ val=(val+solve(i,right))%Mod; i=right; } } printf("%lld %d\n",ans+1,val); return 0; }