【题解】P4747 [CERC2017]Intrinsic Interval

首先题上说的区间的充要条件是 \(max-min=r-l\)

但是这个不是很好维护,我们转化成 区间内的相邻权值对数 \(=r-l\)

由于是排列,可以直接存一下每个值得的出现位置,离线之后用扫描线+线段树维护这个东西。

由于相邻权值对数最大就是 \(r-l\) 所以我们只维护一个最大值就可以了。

线段树上把每个位置 \(i\) 的初值设为 \(i\) ,每有相邻权值对就加 \(1\) ,如果枚举到 \(r\) 时权值等于 \(r\) 就说明 \([l,r]\) 合法。

这样我们就可以使用线段树来找到所有合法的区间。

再看怎么找包含一个区间的最短的合法区间。

容易发现这个合法区间满足一个性质:若 \(p\)\(q\) 都是合法区间,并且 \(p\cap q\neq\emptyset\) ,则 \(p\cap q\) 也是合法区间。

于是我们就有了求出最短合法区间的方法:从左到右枚举区间右端点,如果当前右端点对应的有左端点包含询问区间,那么当前右端点和对应的最大的包含当前询问区间的左端点形成的合法区间就是答案。

证明也很简单,如果当前区间为 \([l_1,r_1]\) ,答案区间为 \(l_2,r_2\) ,显然 \(l_1\leq l_2\leq r_1\leq r_2\) 那么 \([l2,r1]\) 也为合法区间且更优,矛盾。

剩下的就很简单了,离线,按右端点排序,从左到右枚举右端点,用线段树维护权值,存一下最大值和最大值最靠右的出现位置,然后处理所有待询问的区间即可。

所有带询问区间可以放到一个按 \(l\) 排序的大根堆里,因为 \(l\) 大的都没有的话 \(l\) 小的也肯定没有。

具体见代码

\(\sf{Code}\)

#include<bits/stdc++.h>
#define N 2001001
#define MAX 2001
using namespace std;
typedef long long ll;
typedef double db;
const ll inf=1e18;
inline void read(ll &ret)
{
	ret=0;char c=getchar();bool pd=false;
	while(!isdigit(c)){pd|=c=='-';c=getchar();}
	while(isdigit(c)){ret=(ret<<1)+(ret<<3)+(c&15);c=getchar();}
	ret=pd?-ret:ret;
	return;
}
ll n,a[N],m,x,y,p[N];
pair<ll,ll>ans[N];
struct que
{
	ll l,r,data;
	inline friend bool operator <(que x,que y)
	{
		return x.l<y.l;
	}
};
vector<que>v[N];
struct node
{
	ll maxn,maxpos,tag;
}seg[N];
inline node operator +(node x,node y)
{
	if(x.maxn>y.maxn)
		return node{x.maxn,x.maxpos,0};
	else
		return node{y.maxn,y.maxpos,0};
}
inline void build(ll pos,ll l,ll r)
{
	if(l==r)
		seg[pos].maxn=l,seg[pos].maxpos=l;
	else
	{
		ll mid=l+r>>1;
		build(pos<<1,l,mid);
		build(pos<<1|1,mid+1,r);
		seg[pos]=seg[pos<<1]+seg[pos<<1|1];
	}
	return;
}
inline void add(ll pos,ll num)
{
	seg[pos].maxn+=num;
	seg[pos].tag+=num;
	return;
}
inline void pushdown(ll pos)
{
	add(pos<<1,seg[pos].tag);
	add(pos<<1|1,seg[pos].tag);
	seg[pos].tag=0;
	return;
}
inline void upgrade(ll pos,ll l,ll r,ll s,ll t,ll num)
{
	if(l>t||r<s)
		return;
	else if(l>=s&&r<=t)
		return add(pos,num);
	pushdown(pos);
	ll mid=l+r>>1;
	upgrade(pos<<1,l,mid,s,t,num);
	upgrade(pos<<1|1,mid+1,r,s,t,num);
	seg[pos]=seg[pos<<1]+seg[pos<<1|1];
	return;
}
inline node query(ll pos,ll l,ll r,ll s,ll t)
{
	if(l>=s&&r<=t)
		return seg[pos];
	ll mid=l+r>>1;
	pushdown(pos);
	if(mid>=s&&mid<t)
		return query(pos<<1,l,mid,s,t)+query(pos<<1|1,mid+1,r,s,t);
	else if(mid>=s)
		return query(pos<<1,l,mid,s,t);
	else
		return query(pos<<1|1,mid+1,r,s,t);
}
priority_queue<que>q;
signed main()
{
	read(n);
	for(int i=1;i<=n;i++)
		read(a[i]),p[a[i]]=i;
	read(m);
	for(int i=1;i<=m;i++)
	{
		read(x);
		read(y);
		v[y].push_back(que{x,y,i});
	}
	build(1,1,n);
	for(int i=1;i<=n;i++)
	{
		if(a[i]>1&&p[a[i]-1]<i)
			upgrade(1,1,n,1,p[a[i]-1],1);
		if(a[i]<n&&p[a[i]+1]<i)
			upgrade(1,1,n,1,p[a[i]+1],1);
		for(int j=0;j<v[i].size();j++)
			q.push(v[i][j]);
		while(!q.empty())
		{
			que tmp=q.top();
			node temp=query(1,1,n,1,tmp.l);
			if(temp.maxn!=i)
				break;
			ans[tmp.data].first=temp.maxpos;
			ans[tmp.data].second=i;
			q.pop();
		}
	}
	for(int i=1;i<=m;i++)
		printf("%lld %lld\n",ans[i].first,ans[i].second);
	exit(0);
}
posted @ 2022-04-22 09:34  CelticOIer  阅读(55)  评论(0编辑  收藏  举报