BZOJ 3173 [Tjoi2013]最长上升子序列 Treap+LIS

题意:链接

方法: Treap+LIS

解析:

刚看到题还蒙了一会,无从下手啊- -!

后来寻思找找规律。

画画样例以及瞎编了两组数据之后发现点问题…

答案递增?

然后才反应过来- -,当我们从小到大插入的时候,插入后的情况是不影响插入前的。

所以这样的话我们可以把整个序列先弄出来?

之后求一个LIS,这时候对于每一个数来说,他都有个LIS值,当然这个可能不是他插入之后的最优解,因为之前的值可能比他插入之后还要大。

所以我们再需要扫一下数组,更新下答案即可。

至于第一部分找序列,用treap实现是很优越的。

第二部分呢?n^2的做法显然这里是过不了的,所以nlogn就好。

大复杂度O(nlogn)

代码:

#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100010
using namespace std;
int size,n,root;
struct node
{
    int l,r,rnd,siz,w,v;
}tr[N];
void pushup(int rt)
{
    tr[rt].siz=tr[tr[rt].l].siz+tr[tr[rt].r].siz+1;
}
void lturn(int &rt)
{
    int t=tr[rt].r;
    tr[rt].r=tr[t].l;
    tr[t].l=rt;
    tr[t].siz=tr[rt].siz;
    pushup(rt);
    rt=t;
}
void rturn(int &rt)
{
    int t=tr[rt].l;
    tr[rt].l=tr[t].r;
    tr[t].r=rt;
    tr[t].siz=tr[rt].siz;
    pushup(rt);
    rt=t;
}
void insert(int &rt,int v)
{
    if(!rt)
    {
        rt=++size;
        tr[rt].l=tr[rt].r=0,tr[rt].siz=1;
        tr[rt].v=size,tr[rt].rnd=rand();
        return;
    }
    tr[rt].siz++;
    if(v<=tr[tr[rt].l].siz+1)
    {
        insert(tr[rt].l,v);
        if(tr[tr[rt].l].rnd<tr[rt].rnd)rturn(rt);
    }else
    {
        insert(tr[rt].r,v-tr[tr[rt].l].siz-1);
        if(tr[tr[rt].r].rnd<tr[rt].rnd)lturn(rt);
    }
}
int tot;
int seq[N];
void getseq(int rt)
{
    if(tr[rt].l!=0)
        getseq(tr[rt].l);
    seq[++tot]=tr[rt].v;
    if(tr[rt].r!=0)
        getseq(tr[rt].r);
}
int f[N],d[N],ans[N];
int print[N];
void getlis()
{
    memset(d,0x3f,sizeof(d));
    d[0]=0;
    d[1]=seq[1],f[1]=1;
    int mx=1;
    for(int i=2;i<=n;i++)
    {
        if(seq[i]>d[mx])
        {
            f[i]=++mx;
            d[mx]=min(d[mx],seq[i]);
        }else
        {
            int l=0,r=mx,ans=0;
            while(l<=r)
            {
                int mid=(l+r)>>1;
                if(d[mid]<seq[i])ans=mid,l=mid+1;
                else r=mid-1;
            }
            f[i]=ans+1;
            d[ans+1]=min(d[ans+1],seq[i]);
        }
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);x++;
        insert(root,x);
    }
    getseq(root);
    getlis();
    for(int i=1;i<=n;i++)print[seq[i]]=f[i];
    for(int i=1;i<=n;i++)print[i]=max(print[i],print[i-1]);
    for(int i=1;i<=n;i++)printf("%d\n",print[i]);
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-13 09:04:15

BZOJ 3173 [Tjoi2013]最长上升子序列 Treap+LIS的相关文章

BZOJ 3173: [Tjoi2013]最长上升子序列( BST + LIS )

因为是从1~n插入的, 慢插入的对之前的没有影响, 所以我们可以用平衡树维护, 弄出最后的序列然后跑LIS就OK了 O(nlogn) -------------------------------------------------------------------- #include<bits/stdc++.h> #define rep(i, n) for(int i = 0; i < n; ++i) #define clr(x, c) memset(x, c, sizeof(x))

BZOJ 3173 Tjoi2013 最长上升子序列 Treap+树状数组

题目大意:给定一个序列,依次将1~n插入,问每次插入之后序列的LIS长度是多少 由于是从小到大插入,因此插入一个数之后显然是不影响之前的答案的 因此我们不妨先用平衡树搞出插入之后的序列,再求一遍LIS即可 注意最后每个点还要对前面的取一下max 因为插入后LIS可能还是之前的序列 蒟蒻的我到底还是把平衡树写挂了... #include <cstdio> #include <cstring> #include <iostream> #include <algorit

BZOJ 3173: [Tjoi2013]最长上升子序列 [splay DP]

3173: [Tjoi2013]最长上升子序列 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1613  Solved: 839[Submit][Status][Discuss] Description 给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.每插入一个数字,我们都想知道此时最长上升子序列长度是多少? Input 第一行一个整数N,表示我们要将1到N插入序列中,接下是N个数字,第k个数字Xk

BZOJ 3173: [Tjoi2013]最长上升子序列

3173: [Tjoi2013]最长上升子序列 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1524  Solved: 797[Submit][Status][Discuss] Description 给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.每插入一个数字,我们都想知道此时最长上升子序列长度是多少? Input 第一行一个整数N,表示我们要将1到N插入序列中,接下是N个数字,第k个数字Xk

bzoj 3173: [Tjoi2013]最长上升子序列(splay)

3173: [Tjoi2013]最长上升子序列 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 1315  Solved: 682 [Submit][Status][Discuss] Description 给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.每插入一个数字,我们都想知道此时最长上升子序列长度是多少? Input 第一行一个整数N,表示我们要将1到N插入序列中,接下是N个数字,第k个数字

BZOJ 3173 [Tjoi2013] 最长上升子序列 解题报告

这个题感觉比较简单,但却比较容易想残.. 我不会用树状数组求这个原排列,于是我只好用线段树...毕竟 Gromah 果弱马. 我们可以直接依次求出原排列的元素,每次找到最小并且最靠右的那个元素,假设这是第 $i$ 次找的,那么这就是原排列的第 $i$ 项,然后我们就把这个元素删去(变成很大的数),再把这个数以左的数都加 1,进行下一轮. 然后就是裸的最长上升子序列啦~~~ 时间复杂度 $O(n\log n)$,空间复杂度 $O(n)$. 1 #include <cstdio> 2 #inclu

BZOJ3173: [Tjoi2013]最长上升子序列 Treap 平衡树

Description 给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.每插入一个数字,我们都想知道此时最长上升子序列长度是多少? Input 第一行一个整数N,表示我们要将1到N插入序列中,接下是N个数字,第k个数字Xk,表示我们将k插入到位置Xk(0<=Xk<=k-1,1<=k<=N) Output N行,第i行表示i插入Xi位置后序列的最长上升子序列的长度是多少. Sample Input 3 0 0 2 Sample Outp

P4309 [TJOI2013]最长上升子序列

题目 P4309 [TJOI2013]最长上升子序列 做法 最长上升序列的求法肯定是烂大街了 水题是肯定的,确定出序列的位置然后套个树状数组就好了(强制在线的话改成线段树维护前缀最值也行) 所以说这题其实难点在与怎么让代码简洁,见识到一个新的\(STL\):\(rope\) My complete code #include<bits/stdc++.h> #include<ext/rope> using namespace std; typedef int LL; const LL

最长公共子序列(LCS)、最长递增子序列(LIS)、最长递增公共子序列(LICS)

最长公共子序列(LCS) [问题] 求两字符序列的最长公共字符子序列 问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列.令给定的字符序列X=“x0,x1,…,xm-1”,序列Y=“y0,y1,…,yk-1”是X的子序列,存在X的一个严格递增下标序列<i0,i1,…,ik-1>,使得对所有的j=0,1,…,k-1,有xij=yj.例如,X=“ABCBDAB”,Y=“BCDB”是X的一个子序列. 考虑最长公共子序列问题如何分解成