Luogu P3143 [USACO16OPEN]钻石收藏家Diamond Collector 题解

又是一个学数据结构学傻了的人

才不会承认是看到Splay,觉得可以写平衡树才进来的呢

Description:

  • 给出一个序列,问排序后,选取两个区间,使其没有重合部分,且每个区间右端点减去左端点不大于k,求这两个区间长度之和的最大值。

    前置技能:FHQ-Treap、线段树 (不会的出门百度)

    Solution:

  • 看数据范围,5e4,很好,O(nlogn)完全可以水过去。那么我们可以放心的考虑FHQ了。因为要最优,所以我们要找到每一个点,在有序序列中对应的满足题目条件的区间的大小,这就可以用FHQ来做了。将每个数插入平衡树之后,只需要两次split然后查询大小就行了。那么我们怎么维护除去这一段区间后,剩下的区间里面最长的呢?线段树是干啥吃的? 很自然的想到钦定这一段区间为开头(左边的全部舍去),那么我们只需要求出右边的最大就好了,那么,线段树,就决定是你了!
  • 综上所述,我们只需要维护一颗平衡树和一颗线段树就好了,下面是代码时间,我会在代码里面将关键点标注以下的。

    AC Code:

#include<bits/stdc++.h>
#define clean(a,i) memset(a,i,sizeof(a))
#define ll long long
#define inl inline
#define il inl void
#define it inl int
#define ill inl ll
#define re register
#define ri re int
#define rl re ll
#define INF 0x3f3f3f3f
using namespace std;
template<class T>il read(T &x)
{
    int f=1;char k=getchar();x=0;
    for(;k>'9'||k<'0';k=getchar()) if(k=='-') f=-1;
    for(;k>='0'&&k<='9';k=getchar()) x=(x<<3)+(x<<1)+k-'0';
    x*=f;
}
const int MAXN = 4e5+5;
//下面是FHQ的基本操作
int n,k,ans,a[MAXN],sz[MAXN],val[MAXN],size[MAXN],rd[MAXN],ch[MAXN][2],cnt;
#define lc(cur) ch[cur][0]
#define rc(cur) ch[cur][1]
il pushup(int cur){size[cur]=size[lc(cur)]+size[rc(cur)]+1;}
it new_node(int a){
    val[++cnt]=a,size[cnt]=1,rd[cnt]=rand();
    return cnt;
}
it merge(int x,int y){
    if(!x||!y) return x+y;
    if(rd[x]<rd[y]){
        rc(x)=merge(rc(x),y);
        pushup(x);return x;
    }
    else{
        lc(y)=merge(x,lc(y));
        pushup(y);return y;
    }
}
il split(int cur,int k,int &x,int &y){
    if(!cur) x=y=0;
    else{
        if(val[cur]<=k) x=cur,split(rc(x),k,rc(x),y);
        else y=cur,split(lc(y),k,x,lc(y));
        pushup(cur);
    }
}
int root,x,y,z;
il insert(int val){split(root,val,x,y),root=merge(merge(x,new_node(val)),y);}
//操作结束
it get_size(int val){//用FHQ来维护对于每一个数,满足条件的区间长度
    ri sz;
    split(root,val-1,x,y),split(y,val+k,y,z),sz=size[y];
    root=merge(merge(x,y),z);
    return sz;
}
//下面是可奈的线段树,用来维护最大值
struct Seg_Tree{
    int max;
}T[MAXN<<2];
#define ls (cur<<1)
#define rs (cur<<1|1)
#define mid ((l+r)>>1)
il updata(int cur,int l,int r,int pos,int k){
    if(l==r) T[cur].max=k;
    else{
        if(pos<=mid) updata(ls,l,mid,pos,k);
        else updata(rs,mid+1,r,pos,k);
        T[cur].max=max(T[ls].max,T[rs].max);
    }
}
it query(int cur,int l,int r,int L,int R){
    if(l>=L&&r<=R) return T[cur].max;
    ri res=0;
    if(L<=mid) res=max(res,query(ls,l,mid,L,R));
    if(R>mid) res=max(res,query(rs,mid+1,r,L,R));
    return res;
}
int main()
{
    srand(2003+6+8);//随机种子
    read(n),read(k);
    for(ri i=1;i<=n;i++) read(a[i]),insert(a[i]);
    sort(a+1,a+1+n);//将其按大小排序,方便后面的操作
    for(ri i=1;i<=n;i++){
        sz[i]=get_size(a[i]);//计算大小
        updata(1,1,n,i,sz[i]);//插入线段树
    }
    for(ri i=1;i<=n;i++) ans=max(ans,sz[i]+query(1,1,n,i+sz[i],n));//这里的右端点可以注意一下,第i个数,算上自己满足条件的区间长度为sz[i],那么去掉这一段之后,左端点的坐标就是i+sz[i],可以画个图感性理解
    printf("%d",ans);
    return 0;
}
  • 这可真是一个毒瘤题解。。。不过说真的,FHQ用来打暴力真的超好用的!建议大家学一下!

原文地址:https://www.cnblogs.com/TheShadow/p/10805668.html

时间: 2024-08-29 17:09:48

Luogu P3143 [USACO16OPEN]钻石收藏家Diamond Collector 题解的相关文章

P3143 [USACO16OPEN]钻石收藏家Diamond Collector[two-pointers]

P3143 [USACO16OPEN]钻石收藏家Diamond Collector 题意要注意一点:有两个陈列架! 如果只有一个陈列架,是很容易的.two-pointers直接从左到右跑一下即可. 如果有两个陈列架,就需要进行答案合并了.做法是这样的: 设两个数组: \(pre\)数组,\(pre[i]\)表示以\([1,i]\)为右端点时区间长度的最大值. \(suf\)数组,\(suf[i]\)表示以\([i,n]\)为左端点时区间长度的最大值. 两个数组分别是前缀最大值和后缀最大值.同样利

洛谷P3143 [USACO16OPEN]钻石收藏家Diamond Collector

题目描述 Bessie the cow, always a fan of shiny objects, has taken up a hobby of mining diamonds in her spare time! She has collected  diamonds () of varying sizes, and she wants to arrange some of them in a pair of display cases in the barn. Since Bessie

洛谷 P3143 [USACO16OPEN]钻石收藏家Diamond Collector

题目描述 Bessie the cow, always a fan of shiny objects, has taken up a hobby of mining diamonds in her spare time! She has collected NN diamonds (N \leq 50,000N≤50,000) of varying sizes, and she wants to arrange some of them in a pair of display cases in

Bzoj 4582 [Usaco2016 Open] Diamond Collector 题解

4582: [Usaco2016 Open]Diamond Collector Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 204  Solved: 136[Submit][Status][Discuss] Description Bessie the cow, always a fan of shiny objects, has taken up a hobby of mining diamonds in her spare time! Sh

bzoj4582[Usaco2016 Open]Diamond Collector

bzoj4582[Usaco2016 Open]Diamond Collector 题意: n个钻石,每个都有一个大小,现在将其装进2个盒子里,每个盒子里的钻石最大的与最小的大小不能超过k,问最多能装多少个.n最大50000. 题解: 我真傻,真的~首先对大小排序,然后找以i为左端点的可装区间,这个操作两个指针就可以搞,我却以为要二分查找.预处理完了,因为不交错的区间肯定比交错的区间优,所以从n到1递推一下从n到i最大的区间大小是多少,然后枚举每个区间,找到当前区间大小加上右端点+1到n中最大的

4582: [Usaco2016 Open]Diamond Collector

4582: [Usaco2016 Open]Diamond Collector Description Bessie the cow, always a fan of shiny objects, has taken up a hobby of mining diamonds in her spare time! She has collected N diamonds (N≤50,000) of varying sizes, and she wants to arrange some of t

Diamond Collector (动态规划)

问题 I: Diamond Collector 时间限制: 1 Sec  内存限制: 64 MB提交: 22  解决: 7[提交][状态][讨论版] 题目描述 Bessie the cow, always a fan of shiny objects, has taken up a hobby of mining diamonds in her spare time! She has collected N diamonds (N≤50,000) of varying sizes, and sh

Diamond Collector

Diamond Collector 题目描述 Bessie the cow, always a fan of shiny objects, has taken up a hobby of mining diamonds in her spare time! She has collected N diamonds (N≤50,000) of varying sizes, and she wants to arrange some of them in a pair of display case

luogu P1549 棋盘问题(2) 题解

luogu P1549 棋盘问题(2) 题解 题目描述 在\(N * N\)的棋盘上\((1≤N≤10)\),填入\(1,2,-,N^2\)共\(N^2\)个数,使得任意两个相邻的数之和为素数. 例如:当\(N=2\)时,有: 其相邻数的和为素数的有: \(1+2,1+4,4+3,2+3\) 当\(N=4\)时,一种可以填写的方案如下: 在这里我们约定:左上角的格子里必须填数字\(1\). 输入输出格式 输入格式: 一个数\(N\) 输出格式: 如有多种解,则输出第一行.第一列之和为最小的排列方