【CF 474E】Pillars

来源:互联网 发布:声鉴软件 编辑:程序博客网 时间:2024/05/01 14:09

【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;}
0 0