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

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

时间: 2024-10-29 19:05:36

【CF 474E】Pillars的相关文章

【CF 520D】Cubes

[CF 520D]Cubes 怎么说呢--英语阅读题+超级大模拟-- 最重要的是知道怎么出来的数据...题意好懂 xy坐标内给出几个单位正方形 以正方形左下点坐标给出 y=0为地面 正方形下面或者左右下方至少存在一个正方形他才能稳定.. 正方形按0~m-1标号 每次只能取出不影响整体结构的正方形 甲乙玩一个游戏 交替取正方形 每取下一个按从左到右的顺序排好 得到一个大数 重点来了! 取出的数是m进制 转换为十进制是最终结果 甲希望结果最大 乙希望结果最小 问结果为多少 甲先取 题意明白了模拟就行

【CF 460C】Present

[CF 460C]Present 二分+贪心 二分最矮高度的最大值 每二分一个遍历看是否可达 可达low = mid+1不可达high = mid-1 可达的判断用贪心即可 改点长度不足时 在改点设置浇水点 同是在i+w设置断水 之后每个点都继承前一个点的浇水量 到i+w时减少i处浇的水即可 代码如下: #include <iostream> #include <cstdio> #include <cstring> #define sz 100000 #define l

【CF 459D】 Pashmak and Parmida&#39;s problem

[CF 459D] Pashmak and Parmida's problem 预处理+线段树求逆序对 新学了树状数组 很适合这题 来一发 代码如下: #include <iostream> #include <cstdio> #include <cstdlib> #include <map> #include <cstring> #define ll long long using namespace std; map <int,int&

【CF 515D】 Drazil and Tiles

[CF 515D] Drazil and Tiles 拓扑的思想 如果满足条件 '.'未遍历完之前肯定存在度为1的点(上下左右仅有一个'.') 遍历到一个1度点u时 将与他连的点v用对应括号括起 此时v也已匹配 入度归零 同时将与v相连的其余点入度减1 不断遍历知道遍历完所有'.' 若能遍历完 则满足条件否则无解或多解 (吐槽一下 原本用的纯粹拓扑思路 想想也是绝对要T的..后来改换BFS 代码如下: #include <iostream> #include <cstdio> #i

【CF 566F】 Clique in the Divisibility Graph

[CF 566F] Clique in the Divisibility Graph 最大团模型的dp 数做点 能约分的一对数间有路 问最大团(最大完全子图) 用最长公共子序列做法 dp出最长路 由于一个数约数的约数也是这个数的约数 所以只要能连起来就是个完全子图 代码如下: #include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <alg

【CF 507E】Breaking Good

[CF 507E]Breaking Good 双条件最短路 每个路有已搭建和未搭建两种状态 需要把经过的路都建起 为经过的路都拆掉 优先经过最少条路 同样少的路走改动(搭建+拆掉)最小的 最短路跑完后把最短的路上的路径标记一下 bfs输出拆除和搭建 在最短路径上的路 未建的搭建 不在的建好的拆掉 通过此题试了一下spfa的一个小优化还有dijkstra的优先队列优化 不过别看spfa加优化快了点 前两天做了个专门卡这个优化的题--想方设法让他多跑就是..HDOJ 4889 有兴趣的可以去瞅瞅 此

【线段树】【树状数组】【CF 121E】幸运数列

1922. [CF 121E]幸运数列 ★★★ 输入文件:cf121e.in 输出文件:cf121e.out 简单对比 时间限制:3 s 内存限制:256 MB [题目描述] 对于欧洲人来说,"幸运数"是指那些十进制只由4或7组成的数.财务员Petya需要维护一个支持如下操作的整数数列: add l r d - 表示将[l, r]区间内的所有数加上一个正整数d(). count l r - 统计[l, r]区间内有多少个"幸运数".() 请你帮助Petya实现它.

【CF Gym100228】Graph of Inversions

Portal --> qwq(貌似是CodeForces Gym 100228 (ECNA2003) - I) Description 对于长度为 \(n\) 的序列 \(A\) ,定义其逆序图 \(G\) 如下:无向图 \(G\)有\(n\) 个节点,编号为 \(0..n-1\) :对于任意的$ 0≤i<j≤n?1$ ,如果有 \(a[i]>a[j]\),那么 \(G\)中存在一条 \(i\)和 \(j\)之间的边.例如:\(A=\{1,3,4,0,2\}, G=\{(0,3),(1,

【CF 172K】k-Maximum Subsequence Sum

题意 给你一个长度为 \(n\) 的序列,有 \(q\) 组询问,每组询问给你 \(l,r,k\),让你在 \([l,r]\) 区间内选出 \(k\) 个不相交子段,使得这些子段包含的所有数的和最大. \(n,q\le 10^5,\space k\le 20\) 题解 这个 \(k=20\) 一看就很有问题啊 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/cf172k.html