Loading

NOIP 模拟 $14\; \text{队长快跑}$

题解 \(by\;zj\varphi\)

一道很妙的 \(dp\) 题,方程状态不好设置,细节也不少

看到数据范围,直接想离散化

\(f_{i,j}\) 表示处理完前 \(i\) 个水晶,其中摧毁的 \(A_i\) 的最小值为 \(j\) 时最多能摧毁多少。

分类讨论

  1. \(A_i\le B_i\) 时,那么要使 \(A_i\) 为最小值,那么就要摧毁它,所以 \(dp_{i,A_i}=\max(dp_{i-1,B_i+1},...dp_{i-1,\max})+1\)
    但前 \(i-1\) 个数也可能有 \(A_i\) 所以也可以不摧毁它,故 \(dp_{i,A_i}=\max(dp_{i,A_i},dp_{i-1,A_i})\)

  2. \(A_i>B_i\) 时,要使 \(A_i\) 为最小值,则 \(dp_{i,A_i}=\max(dp_{i-1,A_i+1},...dp_{i-1,\max})+1\)
    \(j\in (B_i,A_i]\) 时,\(dp_{i,j}=dp_{i-1,j}+1\)

对于第二种情况,我们要把其中的 \(dp_{i,A_i}\) 取最大值,至于为什么可以在不确定前 \(i\) 个数是否有 \(j\) 时就转移 \(j\)

是因为若没有 \(j\),\(dp_{i,A_i}\) 绝对不会劣于 \(dp_{i,j}\),所以 \(dp_{i,j}\) 的会被 \(dp_{i,A_i}\) 覆盖。

数据范围太大,无法直接转移,考虑线段树优化,转移的式子可以直接当作 单点修改,区间最大值,区间加法。

Code
#include<bits/stdc++.h>
#define ri register signed
#define p(i) ++i
using namespace std;
namespace IO{
    char buf[1<<21],*p1=buf,*p2=buf;
    #define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++
    template<typename T>inline void read(T &x) {
        ri f=1;x=0;register char ch=gc();
        while(ch<'0'||ch>'9') {if (ch=='-') f=0;ch=gc();}
        while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
        x=f?x:-x;
    }
}
using IO::read;
namespace nanfeng{
    #define cmax(x,y) ((x)>(y)?(x):(y))
    #define cmin(x,y) ((x)>(y)?(y):(x))
    #define FI FILE *IN
    #define FO FILE *OUT
    static const int N=1e5+7;
    int a[N],b[N],tmpab[N<<1],n,ans,cnt;
    struct Seg{
        #define ls(x) (x<<1)
        #define rs(x) (x<<1|1)
        #define up(x) T[x].nm=cmax(T[ls(x)].nm,T[rs(x)].nm)
        struct segmenttree{int lz,nm;}T[N<<3];
        inline void down(int x) {
            if (!T[x].lz) return;
            int l=ls(x),r=rs(x);
            T[l].nm+=T[x].lz,T[l].lz+=T[x].lz;
            T[r].nm+=T[x].lz,T[r].lz+=T[x].lz;
            T[x].lz=0;
        }
        void update(int x,int l,int r,int lt,int rt) {
            if (l<=lt&&rt<=r) {p(T[x].nm),p(T[x].lz);return;}
            int mid(lt+rt>>1);
            down(x);
            if (l<=mid) update(ls(x),l,r,lt,mid);
            if (r>mid) update(rs(x),l,r,mid+1,rt);
            up(x);
        }
        void updates(int x,int p,int k,int l,int r) {
            if (l==r) {T[x].nm=cmax(T[x].nm,k);return;}
            int mid(l+r>>1);
            down(x);
            if (p<=mid) updates(ls(x),p,k,l,mid);
            else updates(rs(x),p,k,mid+1,r);
            up(x);
        }
        int query(int x,int l,int r,int lt,int rt) {
            if (l<=lt&&rt<=r) return T[x].nm;
            int mid(lt+rt>>1),res(0);
            down(x);
            if (l<=mid) res=max(res,query(ls(x),l,r,lt,mid));
            if (r>mid) res=max(res,query(rs(x),l,r,mid+1,rt));
            return res;
        }
    }T;
    inline int main() {
        // FI=freopen("nanfeng.in","r",stdin);
        // FO=freopen("nanfeng.out","w",stdout);
        read(n);
        for (ri i(1);i<=n;p(i)) {
            read(a[i]),read(b[i]);
            tmpab[p(cnt)]=a[i],tmpab[p(cnt)]=b[i];
        }
        sort(tmpab+1,tmpab+cnt+1);
        ri kab=unique(tmpab+1,tmpab+cnt+1)-tmpab;
        for (ri i(1);i<=n;p(i)) {
            a[i]=lower_bound(tmpab+1,tmpab+kab,a[i])-tmpab;
            b[i]=lower_bound(tmpab+1,tmpab+kab,b[i])-tmpab;
        }
        kab-=1;
        for (ri i(1);i<=n;p(i)) {
            int tmp=0;
            if (a[i]<=b[i]) {
                if (b[i]<kab) tmp=T.query(1,b[i]+1,kab,1,kab);
                T.updates(1,a[i],tmp+1,1,kab);

            } else {
                T.update(1,b[i]+1,a[i],1,kab);
                if (a[i]<kab) tmp=T.query(1,a[i]+1,kab,1,kab);
                T.updates(1,a[i],tmp+1,1,kab);
            }
        }
        printf("%d\n",T.T[1].nm);
        return 0;
    } 
}
int main() {return nanfeng::main();}
posted @ 2021-07-14 12:08  ナンカエデ  阅读(71)  评论(0编辑  收藏  举报