zoukankan      html  css  js  c++  java
  • 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用来打暴力真的超好用的!建议大家学一下!
  • 相关阅读:
    算法与时间复杂度
    Pandas库
    数据分析之Numpy、Matplotlib库
    增量式爬虫
    分布式爬虫
    scrapy框架之CrawlSpider全站自动爬取
    django 修改 request 对象中的请求参数, 并重新赋值给 request 对象
    nginx 请求文件 进行用户认证/鉴权: internal(限制为内部调用)
    Django 缓存配置的多种方式
    Python 加入类型检查
  • 原文地址:https://www.cnblogs.com/TheShadow/p/10805668.html
Copyright © 2011-2022 走看看