【CF 474E】Pillars

【CF 474E】Pillars

离散化+线段树dp

大半夜写出来了。。。好长好长好长好长好挫……先把高度排序离散化 我又开了个哈希数组用来查某点对应离散后的点 然后遍历每个点时二分出满足题意的区间(1~h-d)(h+d~max) 然后线段树查两个区间当前最大长度的序列 累计到当前点对应的树内点 同时更新他的父亲点们的最大长度 再把之前最大长度的末尾作为当前点的前驱 如果没有就用当前点自己做前驱 最后输出树根存的节点的前驱们(即为树内最长的序列) 各种节点哈希的有点混乱……代码……看乱了就别看了= =我现在看的都有点乱

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
#define sz 100000
#define sr "%lld"

using namespace std;

typedef struct Hash//离散化后哈希用
{
    LL h;//当前点高度
    int id;//离散前节点号
    bool operator < (const struct Hash a)const
    {
        return h < a.h;
    }
}Hash;

typedef struct Node//线段树
{
    int len,Maxid,id;
}Node;

Node lt[sz*4+100];//线段树
Hash hs[sz+2];//离散
int to[sz+2];//记录某点离散后对应点序号
int pre[sz+2];//前驱
int n;

void ReBuild(int site,int l,int r)//初始化线段树
{
    if(l == r)//叶子节点初始长度0 节点号为离散后标号 最大序列节点为自己
    {
        lt[site].len = 0;
        lt[site].Maxid = site;
        lt[site].id = l;
        return;
    }
    int m = (l+r)>>1;
    ReBuild(site<<1,l,m);
    ReBuild(site<<1|1,m+1,r);
    lt[site].len = lt[site].Maxid = 0;//树内节点长度 最大序列节点初始0
}

int LowerBound(LL x)//二分查<= h-d的最大下标
{
    int l = 1,r = n,mid,ans = 0;
    while(l <= r)
    {
        mid = (l+r)>>1;
        if(hs[mid].h <= x)
        {
            ans = mid;
            l = mid+1;
        }else r = mid-1;
    }
    return ans;
}

int UpperBound(LL x)//二分查>= h+d的最小下标
{
    int l = 1,r = n,mid,ans = 0;
    while(l <= r)
    {
        mid = (l+r)>>1;
        if(hs[mid].h >= x)
        {
            ans = mid;
            r = mid-1;
        }else l = mid+1;
    }
    return ans;
}

int Find(int site,int l,int r,int ll,int rr)//找当前点前的最长序列
{
    if(l == ll && r == rr)
    {
        return lt[site].Maxid;//返回当前结点最长序列下标
    }
    int mid = (l+r)>>1;
    int a,b;
    if(mid >= rr) return Find(site<<1,l,mid,ll,rr);
    else if(mid < ll) return Find(site<<1|1,mid+1,r,ll,rr);
    else
    {
        a = Find(site<<1,l,mid,ll,mid);
        b = Find(site<<1|1,mid+1,r,mid+1,rr);
        if(lt[a].len > lt[b].len) return a;
        else return b;
    }
}

void Updata(int site,int l,int r,int id,int mid)//更新树内当前点之前最长序列
{
    if(l == r && l == id)
    {
        lt[site].len = lt[mid].len+1;
        if(mid)//没有更新当前点前驱为自己
        {
            pre[hs[id].id] = hs[lt[mid].id].id;
        }else pre[hs[id].id] = hs[id].id;//否则为最长序列末端
        return;
    }
    int md = (l+r)>>1;
    if(md >= id)
    {
        Updata(site<<1,l,md,id,mid);
        if(lt[site].len < lt[site<<1].len)
        {
            lt[site].Maxid = lt[site<<1].Maxid;
            lt[site].len = lt[site<<1].len;
        }
    }
    else
    {
        Updata(site<<1|1,md+1,r,id,mid);
        if(lt[site].len < lt[site<<1|1].len)
        {
            lt[site].Maxid = lt[site<<1|1].Maxid;
            lt[site].len = lt[site<<1|1].len;
        }
    }
}

void Print(int x)//递归输出最长序列
{
    if(x == pre[x])
    {
        printf("%d",x);
        return;
    }
    Print(pre[x]);
    printf(" %d",x);
}

int main()
{
    int d,i,x1,x2,l;

    scanf("%d %d",&n,&d);
    for(i = 1; i <= n; ++i)//建立哈希数组
    {
        scanf(sr,&hs[i].h);
        hs[i].id = i;
    }

    if(d == 0)//特判距离0直接1~n输出
    {
        printf("%d\n",n);
        for(i = 1; i < n; ++i) printf("%d ",i);
        printf("%d\n",n);
        return 0;
    }


    sort(hs+1,hs+1+n);//按高度对哈希数组排序
    for(i = 1; i <= n; ++i)//安插哈希节点
    {
        to[hs[i].id] = i;
    }
    ReBuild(1,1,n);//建树
    lt[0].len = lt[0].Maxid = 0;

    for(i = 1; i <= n; ++i)
    {
        l = LowerBound(hs[to[i]].h-d);//二分找h-d
        if(l) x1 = Find(1,1,n,1,l);//找到则查到最长序列
        else x1 = 0;

        l = UpperBound(hs[to[i]].h+d);//二分找h+d
        if(l) x2 = Find(1,1,n,l,n);
        else x2 = 0;

        if(lt[x1].len > lt[x2].len) Updata(1,1,n,to[i],x1);//用最长序列更新树内当前结点
        else Updata(1,1,n,to[i],x2);
    }
    printf("%d\n",lt[1].len);
    Print(hs[lt[lt[1].Maxid].id].id);
    puts("");
    return 0;
}

你可能感兴趣的:(线段树dp)