Luogu P3143 [USACO16OPEN]鉆石收藏家Diamond Collector 題解
阿新 • • 發佈:2019-05-03
struct 數據 open pre eap res 1+n 個數 題解
又是一個學數據結構學傻了的人
才不會承認是看到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
用來打暴力真的超好用的!建議大家學一下!
Luogu P3143 [USACO16OPEN]鉆石收藏家Diamond Collector 題解