题解:CF2121H Ice Baby

fif_ifi 表示答案长度为 iii 时,结尾的最小值。因此当 fi−1≤rif_{i - 1} \le r_ifi1ri 时可以进行转移 fi=max⁡(fi−1,li)f_i = \max (f_{i - 1},l_i)fi=max(fi1,li)。于是可以得到 O(n2)O(n^2)O(n2) 的代码:

for (int i = 1;i <= n;++i)
{
    for (int j = i;j;--j)
    {
        if (dp[j - 1] > r[i]) continue;
        dp[j] = min (dp[j],max (dp[j - 1],l[i]));
        ans = max (ans,j);
    }
    cout<<ans<<" \n"[i == n];
}

按照官方题解的思路可以用 multiset 优化到 O(nlog⁡n)O(n \log n)O(nlogn),在此不赘述。


换一种角度,设 fif_ifi 表示最后一个数字为 iii 时的最大长度。则在遇到第 kkk 个区间 [lk,rk][l_k,r_k][lk,rk] 时,有 flk=max⁡j∣j≤lk{fj}+1f_{l_k} = \max\limits_{j \mid j \le l_k}\{f_j\} + 1flk=jjlkmax{fj}+1,线段树维护最大值即可。而 flk+1∼frf_{l_k + 1} \sim f_rflk+1fr 就可以保持原来的数字不变,相当于区间加一。最后的答案就是全局最大值。

又因为值域较大,需要先进行离散化操作。最后,我们可以得到线段树优化到 O(nlog⁡n)O (n \log n)O(nlogn) 代码:

#include <bits/stdc++.h>
#define init(x) memset (x,0,sizeof (x))
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define pii pair <int,int>
using namespace std;
const int MAX = 4e5 + 5;
const int MOD = 1e9 + 7;
inline int read ();
int tree[MAX << 2],tmp[MAX << 2];
void pushup (int cur) {tree[cur] = max (tree[cur << 1],tree[cur << 1 | 1]);}
void pushdown (int cur)
{
    if (!tmp[cur]) return ;
    tree[cur << 1] += tmp[cur];tree[cur << 1 | 1] += tmp[cur];
    tmp[cur << 1] += tmp[cur];tmp[cur << 1 | 1] += tmp[cur];
    tmp[cur] = 0;
}
void build (int cur,int l,int r)
{
    tree[cur] = tmp[cur] = 0;
    if (l == r) return ;
    int mid = (l + r) >> 1;
    build (cur << 1,l,mid);build (cur << 1 | 1,mid + 1,r);
}
void modify1 (int cur,int l,int r,int x,int v)
{
    if (l == r) {tree[cur] = v;return ;}
    int mid = (l + r) >> 1;
    pushdown (cur);
    if (x <= mid) modify1 (cur << 1,l,mid,x,v);
    else modify1 (cur << 1 | 1,mid + 1,r,x,v);
    pushup (cur);
}
void modify2 (int cur,int l,int r,int x,int y)
{
    if (y < l || x > r) return ;
    if (x <= l && y >= r) {++tree[cur];++tmp[cur];return ;}
    int mid = (l + r) >> 1;
    pushdown (cur);
    if (x <= mid) modify2 (cur << 1,l,mid,x,y);
    if (y > mid) modify2 (cur << 1 | 1,mid + 1,r,x,y);
    pushup (cur);
}
int query (int cur,int l,int r,int x,int y)
{
    if (y < l || x > r) return 0;
    if (x <= l && y >= r) return tree[cur];
    int mid = (l + r) >> 1,res = 0;
    pushdown (cur);
    if (x <= mid) res = max (res,query (cur << 1,l,mid,x,y));
    if (y > mid) res = max (res,query (cur << 1 | 1,mid + 1,r,x,y));
    return res;
}
void solve ()
{
    int n = read ();
    vector <int> l (n + 1),r (n + 1),num;
    for (int i = 1;i <= n;++i) l[i] = read (),r[i] = read (),num.push_back (l[i]),num.push_back (r[i]);
    sort (num.begin (),num.end ());
    num.erase (unique (num.begin (),num.end ()),num.end ());
    int tot = num.size ();build (1,1,tot);
    //f[i] 表示最后一个数字为 i 时的最大长度
    for (int i = 1;i <= n;++i)
    {
        l[i] = lower_bound (num.begin (),num.end (),l[i]) - num.begin () + 1;
        r[i] = lower_bound (num.begin (),num.end (),r[i]) - num.begin () + 1;
        modify1 (1,1,tot,l[i],query (1,1,tot,1,l[i]) + 1);
        modify2 (1,1,tot,l[i] + 1,r[i]);
        cout<<tree[1]<<" \n"[i == n];
    }
}
int main ()
{
    int t = read ();
    while (t--) solve ();
    return 0;
}
inline int read ()
{
    int s = 0;int f = 1;
    char ch = getchar ();
    while ((ch < '0' || ch > '9') && ch != EOF)
    {
        if (ch == '-') f = -1;
        ch = getchar ();
    }
    while (ch >= '0' && ch <= '9')
    {
        s = s * 10 + ch - '0';
        ch = getchar ();
    }
    return s * f;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值