小程式之——五級聯動日期選擇器
阿新 • • 發佈:2021-08-04
一、題目
二、解法
理解本題給出序列的方式很重要,我們把它放在座標軸上,那麼我們列舉一個轉折點,然後找它後面最高的轉折點,求最大差值就可以求出最長上升子序列長度,關鍵在於求出子序列個數。
不難發現不同的最低點和最高點組合範圍一定不交,如果相交可以通過調整獲得更優解,所以每一段可以單獨處理。
座標的範圍達到了 \(1e9\),肯定往矩陣加速上想,正常遞推的順序是按照 \(x\) 軸從左往右,可以記錄以這個點結尾的方案數,但是硬要這麼轉移你需要記錄每個 \(y\) 結尾的方案數,那這做錘子。
問題在於無法記錄 \(y\),那麼我們按 \(y\) 的順序遞推不就不需要記錄它了嗎?所以設計狀態 \(f_{v,i}\)
注意每一段要保證左閉右閉,所以端點一定要特別注意,還有如果答案長度為 \(1\) 需要特判。
三、總結
走到絕境的時候應該思考,\(dp\) 狀態、轉移等是否合理,不要受困於定式思維。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define int long long const int M = 55; const int MOD = 998244353; int read() { int x=0,f=1;char c; while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;} while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } int n,m,k,s,ans,res,a[M],l[M],r[M],d[M*M]; struct mat { int a[M][M]; void clear() {memset(a,0,sizeof a);} mat() {clear();} mat operator * (const mat &b) const { mat r; for(int i=0;i<=m;i++) for(int j=0;j<=m;j++) for(int k=0;k<=m;k++) r.a[i][k]=(r.a[i][k]+a[i][j]*b.a[j][k])%MOD; return r; } void print() { for(int i=0;i<=m;i++,puts("")) for(int j=0;j<=m;j++) printf("%d ",a[i][j]); } }A,B; mat qkpow(mat a,int b) { mat r; for(int i=0;i<=m;i++) r.a[i][i]=1; while(b>0) { if(b&1) r=r*a; a=a*a; b>>=1; } return r; } void solve(int L,int R) { m=k=0; for(int i=L,t=s;i<=R;t+=a[i],i++) { l[++m]=(a[i]>0)?t+1:t-1; r[m]=t+a[i]; if(m==1) l[1]=s; d[++k]=l[m],d[++k]=l[m]-1,d[++k]=l[m]+1; d[++k]=r[m],d[++k]=r[m]-1,d[++k]=r[m]+1; } sort(d+1,d+1+k); A.clear();A.a[0][0]=1; k=unique(d+1,d+1+k)-d-1; for(int i=1,ls=s-1;i<=k;i++) { B.clear(); if(d[i]<=ls || d[i]>s+ans) continue; for(int j=1;j<=m;j++) if(min(l[j],r[j])<=ls+1 && max(l[j],r[j])>=d[i]) { for(int k=0;k<=j;k++) B.a[k][j]=1; if(l[j]>=r[j]) B.a[j][j]=0; } A=A*qkpow(B,d[i]-ls);ls=d[i]; } for(int i=0;i<=m;i++) res=(res+A.a[0][i])%MOD; } signed main() { n=read();read(); for(int i=1;i<=n;i++) { a[i]=read(); if(!a[i]) {i--;n--;continue;} } for(int i=1;i<=n;s+=a[i],i++) for(int j=i,t=s;j<=n;j++) { t+=a[j]; ans=max(ans,t-s); } if(!ans) { printf("1 %lld\n",(-s+1)%MOD); return 0; } s=0; for(int i=1;i<=n;s+=a[i],i++) { int r=-1; for(int j=i,t=s;j<=n;j++) { t+=a[j]; if(t-s==ans) r=j; } if(r>0) solve(i,r),i=r; } printf("%lld %lld\n",ans+1,res); }