洛谷 P4093 [HEOI2016/TJOI2016]序列

题目描述

佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他。玩具上有一个数列,数列中某些项的值可能会变化,但同一个时刻最多只有一个值发生变化。现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可 。

注意:每种变化最多只有一个值发生变化。在样例输入1中,所有的变化是:

1 2 3

2 2 3

1 3 3

1 1 3

1 2 4

选择子序列为原序列,即在任意一种变化中均为不降子序列在样例输入2中,所有的变化是:

3 3 3

3 2 3

选择子序列为第一个元素和第三个元素,或者第二个元素和第三个元素,均可满足要

输入输出格式

输入格式:

输入的第一行有两个正整数n, m,分别表示序列的长度和变化的个数。接下来一行有n个数,表示这个数列原始的状态。接下来m行,每行有2个数x, y,表示数列的第x项可以变化成y这个值。1 <= x <= n。

输出格式:

输出一个整数,表示对应的答案

输入输出样例

输入样例#1:

3 4

1 2 3

1 2

2 3

2 1

3 4

输出样例#1:

3

说明

对于20%数据所有数字均为正整数,且小于等于300

对于50%数据所有数字均为正整数,且小于等于3,000

对于100%数据所有数字均为正整数,且小于等于100,000



我们设\(maxv_i\)为第\(i\)个数变化的最大值;\(minv_i\)为第\(i\)个数变化的最小值,\(a_i\)位原来的数值

则题目要求转化为,求一个最长的序列,使一下条件满足

  • \(j<i\)
  • \(max(a_j,maxv_j) \le a_i\)
  • \(a_j\le min(a_i,minv_i)\)

那这种不等式问题就能转化为二维数点问题

对于每一个j,我们每一次就可以在平面内加一个坐标为\((maxv_j,a_j)\)的权值为\(dp_j\)点,对于每一次转移,可以从\((0,0)\)到\((a_i,minv_i)\)中找到一个点权最大的点,当前点答案就是找到点的权值+1。

找点就能用很多数据结构维护,cdq分制树套树,k-d tree都可以,这里我用了树状数组套treap,相比于套动态开点线段树,平衡树比线段树空间更小,空间复杂度\(O(nlogn)\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<stack>
#include<vector>
#include<cstring>
#include<queue>
#include<bitset>
using namespace std;
const int maxn=150000+23333;
typedef long long ll;
inline int read(){
    int an=0,f=1;
    char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch<=‘9‘&&ch>=‘0‘){an=an*10+(ch^48);ch=getchar();}
    return an*f;
}
/*
j<i
a_j<a_i
a_j<min_{a_i}
max_{a_j}<a_i
f_i=max(f_i,f_j+1)
*/
int n,m;
int a[maxn],b[maxn],c[maxn],maxv;
int dp[maxn];
struct Treap{
    int ran[maxn<<3],l[maxn<<3],r[maxn<<3],v[maxn<<3],w[maxn<<3],cnt,ma[maxn<<3];
    inline void update(int k){ma[k]=max(ma[r[k]],max(w[k],ma[l[k]]));}
    inline void l_change(int &k){int t=r[k];r[k]=l[t];l[t]=k;update(k);update(t);k=t;}
    inline void r_change(int &k){int t=l[k];l[k]=r[t];r[t]=k;update(k);update(t);k=t;}
    inline void insert(int &k,int x,int val){
        if(!k){cnt++;k=cnt;ran[k]=rand();ma[k]=w[k]=val;v[k]=x;return;}
        else if(v[k]==x)w[k]=max(w[k],val);
        else if(x<v[k]){insert(l[k],x,val);if(ran[l[k]]<ran[k])r_change(k);}
        else {insert(r[k],x,val);if(ran[r[k]]<ran[k])l_change(k);}
        update(k);
    }
    inline int query(int k,int x){
        int re=0;
        for(;k;){
            if(v[k]>x)k=l[k];
            else re=max(re,max(ma[l[k]],w[k])),k=r[k];
        }
        return re;
    }\\前驱最大
}t;
struct BIT{
    int root[maxn];
    inline int query(int k,int x){
        int re=0;
        for(;k;k-=k&-k)
        re=max(re,t.query(root[k],x));
        return re;
    }
    inline void add(int k,int x,int val){
        for(;k<=maxv;k+=k&-k)t.insert(root[k],x,val);
    }
}T;
int ans;
int main(){
    srand(233333);
    n=read();m=read();
    for(int i=1;i<=n;i++)b[i]=c[i]=a[i]=read();
    for(int i=1;i<=m;i++){
        int x=read(),y=read();
        b[x]=min(b[x],y);c[x]=max(c[x],y);
    }
    for(int i=1;i<=n;i++)maxv=max(maxv,c[i]);
    for(int i=1;i<=n;i++){
        int x=T.query(b[i],a[i]);
        dp[i]=x+1;
        T.add(a[i],c[i],dp[i]);
        ans=max(ans,dp[i]);
    }
    cout<<ans;
    return 0;
}

树套树就是一中思想,具体实现要靠自己领悟


原文地址:https://www.cnblogs.com/ck666/p/8442980.html

时间: 2024-11-04 17:30:17

洛谷 P4093 [HEOI2016/TJOI2016]序列的相关文章

洛谷P4093 [HEOI2016/TJOI2016]序列

题目描述 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他.玩具上有一个数列,数列中某些项的值可能会变化,但同一个时刻最多只有一个值发生变化.现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可 . 注意:每种变化最多只有一个值发生变化.在样例输入1中,所有的变化是: 1 2 3 2 2 3 1 3 3 1 1 3 1 2 4 选择子序列为原序列,即在任意一种变化中均为不降子序列在

题解 P4093 【[HEOI2016/TJOI2016]序列】

这道题原来很水的? noteskey 一开始以为是顺序的 m 个修改,然后选出一段最长子序列使得每次修改后都满足不降 这 TM 根本不可做啊! 于是就去看题解了,然后看到转移要满足的条件的我发出了黑人问号... 然后才发现原来是求的子序列是满足任意一次修改后不降... 于是列出两(san)个条件式子,就可以 CDQ 切掉了 QWQ \(j<i\) \(a_j<min_i\) \(max_j<a_i\) 这里的 max 和 min 就是某个位置上出现过的最 大/小 值 watch out

洛谷 P2023 [AHOI2009]维护序列

P2023 [AHOI2009]维护序列 题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值. 输入输出格式 输入格式: 第一行两个整数N和P(1≤P≤1000000000). 第二行含有N个非负整数,从左到右依次为a1,a2,…,aN, (0≤ai≤100

【[HEOI2016/TJOI2016]序列】

压行真漂亮 首先这肯定是一个\(dp\)了 设\(dp_i\)表示\(i\)结尾的最长不下降子序列的长度 显然我们要找一个\(j\)来转移 也就是\(dp_i=max(dp_j+1)\) 那么什么样的\(j\)满足条件呢 首先得是\(j<i\) 我们还注意到一个条件就是这个序列里最多也只有一个位置会发生变化 可能是\(i\)这个位置发生变化,那么显然需要满足对于任意的\(a_i\)都需要满足大于等于\(val_j\) 于是就有\(val_j<=min_i\) 自然也有可能是前面的\(j\)发生

luogu4093 [HEOI2016/TJOI2016]序列

因为一个变化只会变化一个值,所以 \(dp[i]=max(dp[j])+1,j<i,maxval_j \leq a[i], a[j] \leq minval_i\) 发现跟二维数点问题挺像,树状数组套线段树爽一爽. #include <iostream> #include <cstdio> using namespace std; int n, m, zdz[100005], zxz[100005], a[100005], uu, vv, cnt, dp[100005], a

「luogu4093」[HEOI2016/TJOI2016]序列

写出dp方程,可以发现转移要满足一个三维偏序,那么可以处理三维偏序的方法优化. CDQ分治: cdq分治和树状数组是好伙伴~ 注意分治的顺序,要保证先求解出所有前驱状态. 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=100010; 4 int n,m,dp[N],maxn; 5 struct Node{ 6 int a,maxv,minv,id; 7 }node[N]; 8 int cmpa(const Node

[HEOI2016/TJOI2016]序列 CDQ分治

---题面--- 题解: 首先我们观察一下,如果一个点对(j, i), 要符合题中要求要满足哪些条件? 首先我们设 j < i 那么有: j < i max[j] < v[i] v[j] < min[i] (注意下面两个式子都是用的v[i],v[j],,,而不是i , j...之前因为这个问题纠结了很久,其实我也不知道我在纠结什么...) 这三个式子是不是很眼熟? 如果式子变成: j < i max[j] < max[i] min[j] < min[i] 是不是就

# [HEOI2016/TJOI2016]序列

题目链接 戳这 Solution 首先考虑最暴力的dp 我们设: \(f[i]\)表示选择\(i\)以后所能形成的满足条件的子序列的最大值 \(minx[i]\)表示\(i\)能转换为的最小值 \(maxx[i]\)表示\(i\)能转换为的最大值 于是转移的条件显然了: \(i>j\) \(minx[i]>=a[j]\) \(a[i]>=maxx[i]\) 对于暴力直接枚举j转移就好了,但却只有50分,想想正解. 条件很明显是三维偏序问题啊.我们可以随便用一些数据结构: 如: cdq分治

洛谷 P2709 BZOJ 3781 小B的询问

题目描述 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数.小B请你帮助他回答询问. 输入输出格式 输入格式: 第一行,三个整数N.M.K. 第二行,N个整数,表示小B的序列. 接下来的M行,每行两个整数L.R. 输出格式: M行,每行一个整数,其中第i行的整数表示第i个询问的答案. 输入输出样例 输入样例#1: 6 4 3 1 3 2 1 1 3