codeforces 1032 H. Ice Baby

题目链接:

https://codeforces.com/contest/2121/problem/H

思路:

  • 显然题目要求从\(1-n\)的每个长度的最长不下降子序列,根据这个基本能猜到求解思路会接近每次在数组最后面添加一个数,这个数能对于我的最好答案产生什么贡献。

  • 定义\(dp[i]\)为以\(i\)结尾的最长不下降子序列的长度。

  • 假设我现在遍历到第\(i\)个数了,前\(i-1\)个数的所有结尾的最长不下降子序列长度我已经维护好了。显然的我暴力更新的话:我第\(i\)个数可以枚举值取\(l[i]\)\(r[i]\)每个值,当我值取\(x\)\(dp[x]=max(dp[x],dp[y]+1);(1 \le y\le x-1)\)。就是相当于前面接上值小于我的最长段。这个部分可以用线段树维护区间\(max\)加速。

  • 然后考虑优化这部分,本质来看如果\(dp[x]\)\(dp[y]\)相同,且满足\(x<y\),那么其实只要有\(dp[x]\)就够了。因为如果可以用\(dp[y]\)更新,那么也一定可以用\(dp[x]\)更新。

  • 那么根据这个性质我们就可以优化上面的过程,只要用\(dp[x]\)更新\(dp[l[i]]\)就行\((1\le x \le l[i]-1)\)。因为假设比\(i\)大的值用的是\((1,l[i]-1)\)\(dp\)值更新,然后这时候因为我\(dp[l]\)的值也用这部分更新了,那其实就不需要更新。同理用大于等于\(l[i]\)\(dp\)值更新,也不需要更新。因为\(dp[l]\)更好,所以我们只要用\(dp[x]\)更新\(dp[l[i]]\)就行\((1\le x \le l[i]-1)\)

  • 然后最后别忘了还能接等于自己的产生+\(1\)的贡献,这里可以用线段树区间加更新。

  • 值域较大,离散化和开点树都可以,我用的开点树。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1000100;
int l[N];
int r[N];
int tree[N<<4];
int tag[N<<4];
int L[N<<4];
int R[N<<4];
int cnt=1;
void up(int i){
    tree[i]=max(tree[L[i]],tree[R[i]]);
}
void lazy(int i,int x){
    tree[i]+=x;
    tag[i]+=x;
}
void down(int i){
    if(tag[i]){
        if(!L[i])L[i]=++cnt;
        if(!R[i])R[i]=++cnt;
        lazy(L[i],tag[i]);
        lazy(R[i],tag[i]);
        tag[i]=0;
    }
}
void addd(int tl,int tr,int x,int l,int r,int i){
    if(tl<=l&&r<=tr){
        lazy(i,x);
    }else{
        int mid=(l+r)/2;down(i);
        if(mid>=tl){
            if(!L[i])L[i]=++cnt;
            addd(tl,tr,x,l,mid,L[i]);
        }
        if(mid<tr){
            if(!R[i])R[i]=++cnt;
            addd(tl,tr,x,mid+1,r,R[i]);
        }
        up(i);
    }
}
void update(int x,int fuck,int l,int r,int i){
    if(l==r){
        tree[i]=max(tree[i],fuck);
    }else{
        int mid=(l+r)/2;down(i);
        if(x<=mid){
            if(!L[i])L[i]=++cnt;
            update(x,fuck,l,mid,L[i]);
        }else{
            if(!R[i])R[i]=++cnt;
            update(x,fuck,mid+1,r,R[i]);
        }
        up(i);
    }
}
int query(int tl,int tr,int l,int r,int i){
    if(tree[i]==0)return 0;
    if(tl<=l&&r<=tr){
        return tree[i];
    }else{
        int mid=(l+r)/2;down(i);
        int ans=0;
        if(mid>=tl){
            ans=max(ans,query(tl,tr,l,mid,L[i]));
        }
        if(mid<tr){
            ans=max(ans,query(tl,tr,mid+1,r,R[i]));
        }
        return ans;
    }
}
void init(){
    while(cnt){
        tree[cnt]=tag[cnt]=L[cnt]=R[cnt]=0;
        cnt--;
    }cnt=1;
}
void solve(){
    int n;cin>>n;init();
    for(int i=1;i<=n;i++){
        cin>>l[i]>>r[i];
    }
    for(int i=1;i<=n;i++){
        int val=query(0,l[i]-1,0,1e9,1);
        update(l[i],val,0,1e9,1);
        addd(l[i],r[i],1,0,1e9,1);
        cout<<tree[1]<<" ";
    }cout<<endl;
}
signed main(){
#ifdef ONLINE_JUDGE
#else
    freopen("in.in","r",stdin);
    freopen("out.out","w",stdout);
#endif
    ios::sync_with_stdio(false);cin.tie(0);
    int T;cin>>T;
    while(T--){
        solve();
    }
}
posted @ 2025-07-03 22:43  cbbdhz  阅读(23)  评论(0)    收藏  举报