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