21.5.13 t1
阿新 • • 發佈:2021-06-25
tag:dp,線段樹,單調棧
設 \(f_i\) 為前 \(i\) 個元素的最小花費,則轉移方程為:
\[f_i = \min_{[i,j]\text{可以放同一個數字}}f_{j-1}+1 \]而 \([i,j]\) 可以放同一個數字就相當於是 \(\max l_i\le\min r_i\)。
所以可以二分找到 \(j\) 的範圍,用 \(ST\) 表可以做到 \(nlogn\)。
然後用個線段樹求出 \(f\)
第二問為方案數,轉移為
for(i) for(合法的j) if(f[j]+1 == f[i]) g[i] += g[j-1]*(minr(j,i)-maxl(j,i)+1)
這個可以用單調棧+線段樹維護:
- sum 表示區間內 \(f_i=\min f\) 的 \(g_i\) 之和
- add 表示區間加標記,配合2個單調棧食用,可以維護 \(g_j\cdot(minr-maxl+1)\)
- ans 表示區間內的答案,\(\sum [f_j=\min f]g_j\cdot(minr_{[j,i]}-maxl_{[j,i]}+1)\)
複雜度 \(O(nlogn)\)
#include<bits/stdc++.h> using namespace std; template<typename T> inline void Read(T &n){ char ch; bool flag=false; while(!isdigit(ch=getchar())) if(ch=='-')flag=true; for(n=ch^48; isdigit(ch=getchar()); n=(n<<1)+(n<<3)+(ch^48)); if(flag) n=-n; } enum{ MAXN = 500005, MOD = 998244353, inf = INT_MAX }; inline int inc(int a, int b){ a += b; if(a>=MOD) a -= MOD; return a; } inline void iinc(int &a, int b){a = inc(a,b);} inline void upd(int &a, long long b){a = (a+b)%MOD;} inline int neg(int a){return a?MOD-a:0;} int stmx[19][MAXN], stmn[19][MAXN], n, Case; int a[MAXN], b[MAXN], f[MAXN], g[MAXN]; inline void stpre(){ for(register int i=1; i<=n; i++) stmx[0][i] = a[i], stmn[0][i] = b[i]; for(register int k=1; k<19; k++) for(register int i=1; i+(1<<k)-1<=n; i++) stmx[k][i] = max(stmx[k-1][i],stmx[k-1][i+(1<<k-1)]), stmn[k][i] = min(stmn[k-1][i],stmn[k-1][i+(1<<k-1)]); } int Log[MAXN]; inline int qmin(int l, int r){ int k = Log[r-l+1]; return min(stmn[k][l],stmn[k][r-(1<<k)+1]); } inline int qmax(int l, int r){ int k = Log[r-l+1]; return max(stmx[k][l],stmx[k][r-(1<<k)+1]); } struct ele{ int mn, sum; inline ele operator +(const ele &k){ ele res; res.mn = min(mn,k.mn); if(mn < k.mn) res.sum = sum; if(mn > k.mn) res.sum = k.sum; if(mn == k.mn) res.sum = inc(sum,k.sum); return res; } }; struct node{ int mn, add, ans, sum; #define mn(x) t[x].mn #define ans(x) t[x].ans #define add(x) t[x].add #define sum(x) t[x].sum }t[MAXN<<2]; inline int lc(int x){return x<<1;} inline int rc(int x){return x<<1|1;} inline void Push_Up(int x){ mn(x) = min(mn(lc(x)),mn(rc(x))); if(mn(lc(x)) < mn(rc(x))) ans(x) = ans(lc(x)), sum(x) = sum(lc(x)); if(mn(lc(x)) > mn(rc(x))) ans(x) = ans(rc(x)), sum(x) = sum(rc(x)); if(mn(lc(x)) == mn(rc(x))) ans(x) = inc(ans(lc(x)),ans(rc(x))), sum(x) = inc(sum(lc(x)),sum(rc(x))); } inline void Add(int x, int add){ iinc(add(x),add); upd(ans(x),1ll*sum(x)*add); } inline void Push_Down(int x){ if(add(x)) Add(lc(x),add(x)), Add(rc(x),add(x)), add(x) = 0; } void Build(int x, int head, int tail){ mn(x) = inf; if(head==tail) return; int mid = head+tail >> 1; Build(lc(x),head,mid); Build(rc(x),mid+1,tail); } void Update(int x, int head, int tail, int loc){ if(head==tail){ sum(x) = ans(x) = g[loc]; mn(x) = f[loc]; return; } Push_Down(x); int mid = head+tail >> 1; if(loc<=mid) Update(lc(x),head,mid,loc); if(mid<loc) Update(rc(x),mid+1,tail,loc); Push_Up(x); } void Add(int x, int head, int tail, int l, int r, int add){ if(l<=head and tail<=r) return Add(x,add); Push_Down(x); int mid = head+tail >> 1; if(l<=mid) Add(lc(x),head,mid,l,r,add); if(mid<r) Add(rc(x),mid+1,tail,l,r,add); Push_Up(x); } ele Query(int x, int head, int tail, int l, int r){ if(l<=head and tail<=r) return (ele){mn(x),ans(x)}; Push_Down(x); int mid = head+tail >> 1; if(r<=mid) return Query(lc(x),head,mid,l,r); if(mid<l) return Query(rc(x),mid+1,tail,l,r); return Query(lc(x),head,mid,l,r)+Query(rc(x),mid+1,tail,l,r); } void check(int x, int head, int tail){ if(head==tail){ printf("%d ",ans(x)); return; } Push_Down(x); int mid = head+tail >> 1; check(lc(x),head,mid); check(rc(x),mid+1,tail); } int q1[MAXN], top1, q2[MAXN], top2; int main(){ // freopen("1.in","r",stdin); // freopen("1.out","w",stdout); Read(n); Read(Case); for(register int i=2; i<=n; i++) Log[i] = Log[i>>1]+1; for(register int i=1; i<=n; i++) Read(a[i]), Read(b[i]); stpre(); Build(1,0,n-1); g[0] = 1; Update(1,0,n-1,0); for(register int i=1; i<=n; i++){ while(top1 and a[q1[top1]] <= a[i]) Add(1,0,n-1,q1[top1-1],q1[top1]-1,a[q1[top1]]), top1--; while(top2 and b[q2[top2]] >= b[i]) Add(1,0,n-1,q2[top2-1],q2[top2]-1,neg(b[q2[top2]])), top2--; Add(1,0,n-1,q1[top1],i-1,neg(a[i])); q1[++top1] = i; Add(1,0,n-1,q2[top2],i-1,b[i]); q2[++top2] = i; int head=1, tail=i; while(head<tail){ int mid = head+tail >> 1; if(qmin(mid,i) >= qmax(mid,i)) tail = mid; else head = mid+1; } ele tmp = Query(1,0,n-1,head-1,i-1); f[i] = tmp.mn+1; g[i] = tmp.sum; if(i<n) Update(1,0,n-1,i); } if(Case==1) cout<<f[n]-1<<'\n'; else cout<<g[n]<<'\n'; return 0; }