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