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;
}