原题链接: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;
}