原题链接:https://www.luogu.com.cn/problem/P4093
题意解读:一个序列,m个变化,求任意一个变化后不受影响的最长上升子序列长度。
解题思路:
设原序列为a[N],原序列经过变化后能得到的最大值序列为maxa[N],最小值序列为mina[N]
设f[i]表示以第i个数结尾的最长不降子序列长度
有f[i] = max(f[j]) + 1,条件是j < i, a[j] <= mina[i], maxa[j] <= a[i]
因此,可以转化为一个三维偏序问题!
借助于树套树,用树状数组套权值线段树,维护对应的位置的最大f值,a,maxa共同代表j
用树状数组维护线段树的根节点root[a[x]],用权值线段树维护maxa[x]对应的maxf值(maxf值就是最大的f[j])
枚举序列,i从1到n
查询已加入树套树的最大f值,也就是查询线段树root[1]~root[a[i]]中值区间<=mina[i]的最大f值,设为res
f[i] = res + 1
然后,将f[i]值更新到root[maxa[i]]~root[MAXA]线段树的值范围包含a[i]的节点的f值,供后面递推查询。
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
struct Node
{
int L, R;
int maxf; //节点表示值域区间[l,r]中最大的f(),也就是以[l,r]中某一个位置的值结尾的最长不降子序列长度
} tr[N * 80];
int root[N], idx;
int a[N], maxa[N], mina[N], MAXA; //a:原数组,maxa:a最大可以变成多大,mina:a最小可以变成多小
int f[N]; //f[i]表示以第i个数结尾的最长不降子序列长度
int n, m, ans;
int lowbit(int x)
{
return x & -x;
}
//在根为u的线段树中查询节点值域<=pos的最大的maxf
int query(int u, int l, int r, int pos)
{
if(l == r) return tr[u].maxf;
int mid = l + r >> 1;
if(pos <= mid) return query(tr[u].L, l, mid, pos);
else return max(tr[tr[u].L].maxf, query(tr[u].R, mid + 1, r, pos));
}
//将根为u的线段树的值域区间包含pos的节点的maxf值更新(如果v更大的话)
int update(int u, int l, int r, int pos, int v)
{
if(!u) u = ++idx;
tr[u].maxf = max(tr[u].maxf, v);
if(l == r) return u;
int mid = l + r >> 1;
if(pos <= mid) tr[u].L = update(tr[u].L, l, mid, pos, v);
else tr[u].R = update(tr[u].R, mid + 1, r, pos, v);
return u;
}
//查询maxa[j]<=x且a[j]<=y的j的最大f[j]值
//采用树套树,树状数组维护第一维,权值线段树维护第二维
int find(int x, int y)
{
int res = 0;
for(int i = x; i; i -= lowbit(i))
{
res = max(res, query(root[i], 1, MAXA, y));
}
return res;
}
//将x, y对应的maxf更新到树套树
//也就是更新所有root[x]~root[MAXA]的线段树的值为y的节点的maxf为v(如果v更大)
void add(int x, int y, int v)
{
for(int i = x; i <= MAXA; i += lowbit(i))
root[i] = update(root[i], 1, MAXA, y, v);
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
cin >> a[i];
maxa[i] = mina[i] = a[i];
}
int x, y;
while(m--)
{
cin >> x >> y;
maxa[x] = max(maxa[x], y);
mina[x] = min(mina[x], y);
MAXA = max(MAXA, maxa[x]);
}
for(int i = 1; i <= n; i++)
{
f[i] = find(mina[i], a[i]) + 1; //f[i] = max(f[j]) + 1,j<i, a[j]<=mina[i], maxa[j]<=a[i]
add(a[i], maxa[i], f[i]); //将f[i]的值更新到这个j对应树套树中的位置,也就是root[a[i]]~root[MAXA]所有线段树的值maxa[i]的maxf更新为f[i]
ans = max(ans, f[i]);
}
cout << ans;
return 0;
}
标签:进阶,int,洛谷题,线段,P4093,mina,序列,maxa,root From: https://www.cnblogs.com/jcwy/p/18654873