洛谷题单指南-线段树的进阶用法-P4093 [HEOI2016/TJOI2016] 序列

原题链接: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[mina[i]]中值区间<=a[i]的最大f值,设为res

f[i] = res + 1

然后,将f[i]值更新到root[a[i]]~root[MAXA]线段树的值范围包含maxa[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;
}

//查询a[j]<=x且maxa[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;
}

 

posted @   五月江城  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示