1. 程式人生 > 其它 >21.5.13 t1

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