【题解】P4747 [CERC2017]Intrinsic Interval
首先题上说的区间的充要条件是 max−min=r−l
但是这个不是很好维护,我们转化成 区间内的相邻权值对数 =r−l
由于是排列,可以直接存一下每个值得的出现位置,离线之后用扫描线+线段树维护这个东西。
由于相邻权值对数最大就是 r−l 所以我们只维护一个最大值就可以了。
线段树上把每个位置 i 的初值设为 i ,每有相邻权值对就加 1 ,如果枚举到 r 时权值等于 r 就说明 [l,r] 合法。
这样我们就可以使用线段树来找到所有合法的区间。
再看怎么找包含一个区间的最短的合法区间。
容易发现这个合法区间满足一个性质:若 p 和 q 都是合法区间,并且 p∩q≠∅ ,则 p∩q 也是合法区间。
于是我们就有了求出最短合法区间的方法:从左到右枚举区间右端点,如果当前右端点对应的有左端点包含询问区间,那么当前右端点和对应的最大的包含当前询问区间的左端点形成的合法区间就是答案。
证明也很简单,如果当前区间为 [l1,r1] ,答案区间为 l2,r2 ,显然 l1≤l2≤r1≤r2 那么 [l2,r1] 也为合法区间且更优,矛盾。
剩下的就很简单了,离线,按右端点排序,从左到右枚举右端点,用线段树维护权值,存一下最大值和最大值最靠右的出现位置,然后处理所有待询问的区间即可。
所有带询问区间可以放到一个按 l 排序的大根堆里,因为 l 大的都没有的话 l 小的也肯定没有。
具体见代码
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);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?