设 fif_ifi 表示答案长度为 iii 时,结尾的最小值。因此当 fi−1≤rif_{i - 1} \le r_ifi−1≤ri 时可以进行转移 fi=max(fi−1,li)f_i = \max (f_{i - 1},l_i)fi=max(fi−1,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(nlogn)O(n \log n)O(nlogn),在此不赘述。
换一种角度,设 fif_ifi 表示最后一个数字为 iii 时的最大长度。则在遇到第 kkk 个区间 [lk,rk][l_k,r_k][lk,rk] 时,有 flk=maxj∣j≤lk{fj}+1f_{l_k} = \max\limits_{j \mid j \le l_k}\{f_j\} + 1flk=j∣j≤lkmax{fj}+1,线段树维护最大值即可。而 flk+1∼frf_{l_k + 1} \sim f_rflk+1∼fr 就可以保持原来的数字不变,相当于区间加一。最后的答案就是全局最大值。
又因为值域较大,需要先进行离散化操作。最后,我们可以得到线段树优化到 O(nlogn)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;
}