CF484E Sign on Fence(主席树+二分答案)

CF484E Sign on Fence

前言

主席树入门

解题思路

这个很好做。首先题目要我们求的是最小值的最大值,显然一眼就是二分答案,我们现在的问题就变成了怎样检验一个答案是否符合要求。

比如说我们二分出来一个 \(mid\),那么我们需要检验在 \([l,r]\) 之中是否存在长度为 \(k\) 的连续不比 \(mid\) 小的串。

由于我们只关心数字与 \(mid\) 的大小关系,我们可以认为大于等于 \(mid\)\(1\),小于 \(mid\)\(0\)

所以我们求的就是对于 \(mid\),最长连续为 \(1\) 的串长度是否大于等于 \(k\)

容易想到对每个 \(mid\) 开线段树,但是时空爆炸,我们需另想妙招。

可以用主席树来优化。

此时我们把从大到小的所有 \(mid\) 对应的线段树全部求出来,最多修改 \(n\) 次,可以接受。

至于怎么维护最长连续 \(1\),可以看其他博文,有详细解释,或者看我的代码也可以。

注意要离散化,把值域修改为 \([1,n]\),否则可能的 \(mid\) 值太多了。

代码

//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
#define int long long//别骂了
using namespace std;

int read() {
	char ch=getchar();
	int f=1,x=0;
	while(ch<'0'||ch>'9') {
		if(ch=='-')
			f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') {
		x=x*10+ch-'0';
		ch=getchar();
	}
	return f*x;
}

const int MAXN=1e5+10;

int n,m,q,cnt,tot;
int num[MAXN],a[MAXN],root[MAXN],rt[MAXN];
vector<int> vec[MAXN];
struct seg_tree {
	int llen,rlen,alen,ls,rs,len;
}s[MAXN<<5];

int new_node() {
	return ++cnt;
}
void pushup(seg_tree &ans,seg_tree a,seg_tree b) {
	ans.len=a.len+b.len;
	
	ans.alen=max(max(a.alen,b.alen),a.rlen+b.llen);
	if(a.llen==a.len) {
		ans.llen=a.llen+b.llen;
	}
	else {
		ans.llen=a.llen;
	}
	if(b.rlen==b.len) {
		ans.rlen=b.rlen+a.rlen;
	}
	else {
		ans.rlen=b.rlen;
	}
}
int build(int l,int r,int p) {
	if(!p) {
		p=new_node();
	}
	if(l==r) {
		s[p].len=r-l+1;
		s[p].llen=s[p].rlen=s[p].alen=0;
		return p;
	}
	int mid=(l+r)>>1;
	s[p].ls=build(l,mid,s[p].ls);
	s[p].rs=build(mid+1,r,s[p].rs);
	pushup(s[p],s[s[p].ls],s[s[p].rs]);
	return p;
}
int modify(int l,int r,int p0,int p,int x) {
	if(!p) {
		p=new_node();
	}
	if(l==r) {
		s[p].len=r-l+1;
		s[p].llen=s[p].rlen=s[p].alen=1;
		return p;
	}
	int mid=(l+r)>>1;
	if(x<=mid) {
		s[p].rs=s[p0].rs;
		s[p].ls=modify(l,mid,s[p0].ls,s[p].ls,x);
	}
	else {
		s[p].ls=s[p0].ls;
		s[p].rs=modify(mid+1,r,s[p0].rs,s[p].rs,x);
	}
	pushup(s[p],s[s[p].ls],s[s[p].rs]);
	return p;
}
seg_tree query(int l,int r,int p,int x,int y) {
	if(y<l||r<x) {
		return s[0];
	}
	if(x<=l&&r<=y) {
		return s[p];
	}
	int mid=(l+r)>>1;
	seg_tree ans=s[0];
	pushup(ans,query(l,mid,s[p].ls,x,y),query(mid+1,r,s[p].rs,x,y));
	return ans;
}

bool check(int l,int r,int k,int x) {
	if(query(1,n,rt[x],l,r).alen>=k) {
		return 1;
	}
	return 0;
}
signed main() {
	cin>>n;
	for(int i=1;i<=n;i++) {
		a[i]=read();
		num[i]=a[i];
	}
	sort(num+1,num+n+1);
	m=unique(num+1,num+n+1)-num-1;
	for(int i=1;i<=n;i++) {
		a[i]=lower_bound(num+1,num+m+1,a[i])-num;
		//cout<<a[i]<<endl;
		vec[a[i]].push_back(i);
	}
	
	root[0]=build(1,n,root[0]);
	for(int i=m;i;i--) {
		int sz=vec[i].size();
		for(int j=0;j<sz;j++) {
			tot++;
			root[tot]=modify(1,n,root[tot-1],root[tot],vec[i][j]);
		}
		rt[i]=root[tot];
	}
	
	cin>>q;
	for(int i=1;i<=q;i++) {
		int l,r,k;
		l=read();r=read();k=read();
		
		int lft=1,rgt=m;
		while(lft+1<rgt) {
			int mid=(lft+rgt)>>1;
			if(check(l,r,k,mid)) {
				lft=mid;
			}
			else {
				rgt=mid;
			}
		}
		if(check(l,r,k,rgt)) {
			printf("%lld\n",num[rgt]);
		}
		else {
			printf("%lld\n",num[lft]);
		}
	}
	return 0;
}
posted @ 2021-06-05 17:13  huayucaiji  阅读(71)  评论(0编辑  收藏  举报