CF484E Sign on Fence 主席树+二分

原题:http://codeforces.com/contest/484/problem/E

题解:要求[l,r]中长度为k的子序列最小值最大。通过分析可以发现,k长的子序列不好求,类似于TJOI2016排序的那一题,二分一个值mid,将所有大于等于mid的都给1,其余都给0,只要查询给定区间最长连读1的长度就好了。尝试用一下线段树,需要对每一个值都建一颗线段树,同时还要不停的修改,这样显然是不好的。不妨将给的数组排序,很容易看出前一颗和后一颗只有一条链不一样,这其实就是主席树。所以先排序,然后一个一个加进主席中,就可以找到>=mid的01序列了,然后就可以快乐的二分了。

在求最长连续1的长度时的技巧,用结构体重载+法符号可以比较省事的合并左右子树

#include<bits/stdc++.h>
#define mid (l+r)/2
using namespace std;
const int N=1e5+10;
const int M=30;
const int MAXN=N*M;
struct node{int a,b;}a[N];
struct data{//要求最长连续1的序列长度重载一下+ 相当于两段的合并 
	int len,mm,lm,rm;
	data(){len=mm=rm=lm=0;}
	data(int L,int a,int b,int c){
		len=L;mm=a;lm=b;rm=c;
	}
	friend data operator + (const data &x,const data &y){
		int len=x.len+y.len;
		int mm=max(x.mm,max(y.mm,x.rm+y.lm));
		int lm=x.lm;if(lm==x.len) lm+=y.lm;
		int rm=y.rm;if(rm==y.len) rm+=x.rm;
		return (data){len,mm,lm,rm};
	}
}tt[MAXN];
int mm[MAXN],lm[MAXN],rm[MAXN],L[MAXN],R[MAXN];
int T[N];
int n,m,tot;
inline int rd(){
	int x=0;int f=1;char s=getchar();
	while(!isdigit(s)) f=(s=='-'?-1:f),s=getchar();
	while(isdigit(s)) x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return x*f; 
}
bool cmp(node x, node y){
	return x.a==y.a?x.b<y.b:x.a>y.a;
}
inline void upd(int rt){
	tt[rt]=tt[L[rt]]+tt[R[rt]];
}

int build(int l,int r){
	int rt=++tot;
	if(l==r) {
		tt[rt]=(data){1,0,0,0};return rt;
	}else{
		L[rt]=build(l,mid);
		R[rt]=build(mid+1,r);
	}
	upd(rt);
	return rt;
}
int update(int pre,int l,int r,int x){
	int rt=++tot;
	tt[rt]=tt[pre];L[rt]=L[pre];R[rt]=R[pre];
	if(l==r) {
		tt[rt]=(data){1,1,1,1};return rt;
	}else{
		if(x<=mid) L[rt]=update(L[pre],l,mid,x);
		else R[rt]=update(R[pre],mid+1,r,x); 
	}	
	upd(rt);
	return rt;
}
data query(int now,int l,int r,int x,int y){
	if(x<=l && r<=y) return tt[now];
	data ans=(data){0,0,0,0};
	if(x<=mid) ans=ans+query(L[now],l,mid,x,y);
	if(mid<y)  ans=ans+query(R[now],mid+1,r,x,y);
	return ans;
}
int query(int L,int R,int k){
	int l=1;int r=n;
	while(l<=r){
		if(query(T[mid],1,n,L,R).mm>=k) r=mid-1;//a中降序排序 
		else l=mid+1;
	}
	return r+1;
}
int main(){
	n=rd();
	for(int i=1;i<=n;i++){
		a[i].a=rd();a[i].b=i;
	}
	sort(a+1,a+n+1,cmp);
	tot=0;
	T[0]=build(1,n);//建成01主席树 
	for(int i=1;i<=n;i++) T[i]=update(T[i-1],1,n,a[i].b); 
	m=rd(); 
	for(int i=1;i<=m;i++){
		int x=rd();int y=rd();int k=rd();
		printf("%d\n",a[query(x,y,k)].a);
	}
	return 0;
} 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值