BZOJ4553: [Tjoi2016&Heoi2016]序列 树套树优化DP

把pos[i]上出现的平常值定义为nor[i]最大值定义为max[i]最小值定义为min[i],那么我们发现在两个值,i(前),j(后),当且仅当max[i]<=nor[j],nor[i]<=min[j]时才会组成序列的前后两个值,并且当序列里所有连续的两个值都满足这个条件是时就可以,因此我们以f[i]表示以i为起点的序列最长值,那么我们就可以转移了f[i]=maxf[j](max[i]<=nor[j],nor[i]<=min[j],pos[i]<pos[j])+1,这就是一个三维逆序对,至于pos[i]我们用默认时间来维护(按倒序插入),用一颗值域线段树来维护nor[i]值域并在每个节点中建一颗替罪羊树表示在nor在此值域里的数,并用min值排序,并且在每个替罪羊节点上维护最大值标记(表示是子树里表示的点里f最大值)

用树套树解决三维偏序的一般思路:一维用时间维护,一维用线段树来维护,另一位用线段树里的平衡排序维护

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstring>
#define MAXN 100000
using namespace std;
inline int read()
{
    int sum=0;
    char ch=getchar();
    while(ch<‘0‘||ch>‘9‘)ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘)
    {
      sum=(sum<<1)+(sum<<3)+ch-‘0‘;
      ch=getchar();
    }
    return sum;
}
inline int MAX(int x,int y)
{
    return x>y?x:y;
}
inline int MIN(int x,int y)
{
    return x<y?x:y;
}
const double alpha=0.75;
struct ScapeGoat_Tree
{
    ScapeGoat_Tree *ch[2];
    int key,size,f,Max;
    void pushup()
    {
         size=ch[0]->size+1+ch[1]->size;
         Max=ch[0]->Max>ch[1]->Max?ch[0]->Max:ch[1]->Max;
         Max=Max>f?Max:f;
    }
    bool isbad()
    {
        return alpha*size+5<ch[0]->size||alpha*size+5<ch[1]->size;
    }
}*null,Node[MAXN<<5],*list[MAXN<<4];
int top,len;
inline void ScapeGoat_Tree_Init()
{
    null=Node;
    null->ch[1]=null->ch[0]=null;
}
inline ScapeGoat_Tree *NEW(int key,int F)
{
    ScapeGoat_Tree *p=&Node[++top];
    p->ch[0]=p->ch[1]=null;
    p->size=1;
    p->key=key;
    p->f=p->Max=F;
    return p;
}
void travel(ScapeGoat_Tree *p)
{
    if(p==null)return;
    travel(p->ch[0]);
    list[++len]=p;
    travel(p->ch[1]);
}
ScapeGoat_Tree *divide(int l,int r)
{
    if(l>r)return null;
    int mid=(l+r)>>1;
    list[mid]->ch[0]=divide(l,mid-1);
    list[mid]->ch[1]=divide(mid+1,r);
    list[mid]->pushup();
    return list[mid];
}
inline void rebuild(ScapeGoat_Tree *&p)
{
    len=0;
    travel(p);
    p=divide(1,len);
}
ScapeGoat_Tree **insert(ScapeGoat_Tree *&p,int key,int F)
{
    if(p==null)
    {
        p=NEW(key,F);
        return &null;
    }
    ScapeGoat_Tree **ret=insert(p->ch[p->key<key],key,F);
    p->pushup();
    if(p->isbad())ret=&p;
    return ret;
}
inline void Insert(ScapeGoat_Tree *&Root,int key,int F)
{
    ScapeGoat_Tree **p=insert(Root,key,F);
    if(*p!=null)rebuild(*p);
}
inline int get_Rank(ScapeGoat_Tree *Root,int key)
{
    ScapeGoat_Tree *p=Root;
    int ret=0;
    while(p!=null)
     if(p->key>=key)
      p=p->ch[0];
     else
      ret+=p->ch[0]->size+1,p=p->ch[1];
    return ret;
}
int get_Max(ScapeGoat_Tree *p,int l)
{
    if(l>p->size)return 0;
    if(l<=1) return p->Max;
    int ans=0;
    if(l<=p->ch[0]->size)ans=get_Max(p->ch[0],l);
    if(l<=p->ch[0]->size+1)ans=MAX(ans,p->f);
    ans=MAX(ans,get_Max(p->ch[1],l-p->ch[0]->size-1));
    return ans;
}
inline int query(ScapeGoat_Tree *Root,int key)
{
    return get_Max(Root,get_Rank(Root,key)+1);
}
struct Seg_Tree
{
    ScapeGoat_Tree *root;
    int l,r,mid;
    Seg_Tree *ch[2];
}node[MAXN<<2],*root;
int sz;
inline Seg_Tree *New(int l,int r)
{
    Seg_Tree *p=&node[sz++];
    p->l=l;
    p->r=r;
    p->mid=(l+r)>>1;
    p->root=null;
    return p;
}
void build(Seg_Tree *p)
{
    if(p->l==p->r)return;
    p->ch[0]=New(p->l,p->mid);
    p->ch[1]=New(p->mid+1,p->r);
    build(p->ch[0]);
    build(p->ch[1]);
}
inline void Seg_Tree_Init()
{
    root=New(1,MAXN);
    build(root);
}
void Ins(Seg_Tree *p,int pos,int key,int F)
{
    Insert(p->root,key,F);
    if(p->l==p->r)return;
    Ins(p->ch[p->mid<pos],pos,key,F);
}
int Query(Seg_Tree *p,int l,int key)
{
    if(l<=p->l)
     return query(p->root,key);
    int ans=0;
    if(l<=p->mid) ans=Query(p->ch[0],l,key);
    ans=MAX(ans,Query(p->ch[1],l,key));
    return ans;
}
int n,m;
int Max[MAXN+10],Min[MAXN+10],nor[MAXN+10],f[MAXN+10];
inline void Init()
{
    ScapeGoat_Tree_Init();
    Seg_Tree_Init();
    n=read(),m=read();
    for(int i=1;i<=n;i++)Max[i]=Min[i]=nor[i]=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        Max[x]=MAX(y,Max[x]);
        Min[x]=MIN(y,Min[x]);
    }
}
inline void WORK()
{
    f[n]=1;
    Ins(root,nor[n],Min[n],1);
    int ans=1;
    for(int i=n-1;i>0;i--)
    {
        f[i]=Query(root,Max[i],nor[i])+1;
        Ins(root,nor[i],Min[i],f[i]);
        ans=MAX(f[i],ans);
    }
    printf("%d",ans);
}
int main()
{
    Init();
    WORK();
}
时间: 2024-08-05 11:12:09

BZOJ4553: [Tjoi2016&Heoi2016]序列 树套树优化DP的相关文章

【BZOJ4553】[Tjoi2016&amp;Heoi2016]序列 cdq分治+树状数组

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

BZOJ4553: [Tjoi2016&amp;Heoi2016]序列

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

BZOJ 4553 Tjoi2016&amp;Heoi2016 序列

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

知识点 - 线段树 权值 树套树 二维 可持续

知识点 - 线段树 权值 树套树 二维 可持续 //区间更新求和 inline int ls(int p) { return p << 1; }//左儿子 inline int rs(int p) { return p << 1 | 1; }//右儿子 void push_up(int p) { t[p] = t[ls(p)] + t[rs(p)]; }// 向上不断维护区间操作 void build(ll p, ll l, ll r) { if (l == r) { t[p] =

bzoj 3295: [Cqoi2011]动态逆序对(树套树 or CDQ分治)

Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. Output 输出包含m行,依次为删除每个元素之前,逆序对的个数. Sample Input 5 4 1 5 3

BZOJ 3489 A simple rmq problem 可持久化树套树

题目大意:给定一个序列,多次询问某一区间中出现且仅出现一次的最大的数 令第i个数左侧第一个与这个数相同的数为last[i] 右侧第一个与这个相同的数为next[i] 那么一个数a[i]在区间内出现一次当且仅当last[i]<l&&next[i]>r&&l<=i<=r 于是我们将元素按照last[i]排序并构建可持久化线段树 令pos为满足last[i]<l的最大的i 每次查询我要查询的是第pos个版本的线段树内所有next[i]>r的数中

hdu 4417 Super Mario/树套树

原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417 题意很简单,给定一个序列求一个区间 [L, R,]中小于等于H的元素的个数. 好像函数式线段树可解吧,可弱弱的沙茶一直没弄懂其精髓,只好用树套树暴力碾压了 额树套树,线段树的每一个节点套一个sb树. 当查询[l,r]区间中的值小于等于H的个数,先用线段树找到相应的区间, 然后再查询该区间下对应的平衡树中小于等于H的个数,累加即可. 一直以为会超时,结果400+ms就过了,数据应该很弱吧(自己对

洛谷3380 二逼平衡树(树套树)

题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询k在区间内的排名 查询区间内排名为k的值 修改某一位值上的数值 查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647) 查询k在区间内的后继(后继定义为严格大于x,且最小的数,若不存在输出2147483647) 注意上面两条要求和tyvj或者bzoj不一样,请注意 输入输出格式 输入格式: 第一行两个数 n,m 表示长度为n的有序序列和m个操作 第二行有n个数,

树套树

树套树,顾名思义,就是用一颗树套在另外一组树之上.比方说我们有一颗树,假如它的每一个结点(抽象意义上,一株树由多个结点组成)也是一株树,那么这就形成了树套树.外部树和内部树可以是不同品种的.一般外部树都是形如线段树或树状数组等用于统计和区间处理但是内存消耗大的树,而内部树则往往是像Splay一样功能强大且内存消耗小的树,当然这不是绝对的. 考虑这样的一个场景,给你一组数值序列S={v1,v2,....},要求你快速求区间[L,R]之间第k小的值,并且数值序列中可能有若干元素会被修改.如何解决?首