【gmoj】【线段树】矮人排队
题目
解题思路
暴力想法就是判断编号A~B的小矮人位置最大值减最小值是否等于B-A
但是时间复杂度达到O(NM)
想到查询位置时可以用线段树优化一丢丢长
代码
暴力
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,x,l,r,a[200010],w[200010];
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
w[a[i]]=i;
}
while (m--)
{
scanf("%d%d%d",&x,&l,&r);
if (x==1)
{
swap(w[a[l]],w[a[r]]);
swap(a[l],a[r]);
}
else {
int mi=1e9,ma=0;
for (int i=l;i<=r;i++)
mi=min(mi,w[i]),ma=max(ma,w[i]);
if (ma-mi==r-l) printf("YES\n");
else printf("NO\n");
}
}
return 0;
}
正解
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int n,m,x,y,z,d1,d2;
int a[200010],w[200010],ma[1000100],mi[1000010];
void build(int l,int r,int k)
{
if (l==r)
{
ma[k]=mi[k]=w[l];
return;
}
int mid=(l+r)/2;
build(l,mid,k*2);
build(mid+1,r,k*2+1);
ma[k]=max(ma[k*2],ma[k*2+1]);
mi[k]=min(mi[k*2],mi[k*2+1]);
}
void change(int l,int r,int k,int q)
{
if (l==r)
{
ma[k]=mi[k]=w[l];
return;
}
int mid=(l+r)/2;
if (q<=mid) change(l,mid,k*2,q);
else change(mid+1,r,k*2+1,q);
ma[k]=max(ma[k*2],ma[k*2+1]);
mi[k]=min(mi[k*2],mi[k*2+1]);
}
int ask1(int x,int y,int l,int r,int k)
{
if (x<=l&&y>=r) return ma[k];
int mid=(l+r)/2,da=0;
if (x<=mid) da=ask1(x,y,l,mid,k*2);
if (y>mid) da=max(da,ask1(x,y,mid+1,r,k*2+1));
return da;
}
int ask2(int x,int y,int l,int r,int k)
{
if (x<=l&&y>=r) return mi[k];
int mid=(l+r)/2,da=1e9;
if (x<=mid) da=ask2(x,y,l,mid,k*2);
if (y>mid) da=min(da,ask2(x,y,mid+1,r,k*2+1));
return da;
}
int main()
{
memset(mi,0x7f,sizeof(mi));
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
w[a[i]]=i;
}
build(1,n,1);
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&z,&x,&y);
if (z==1)
{
swap(a[x],a[y]);
w[a[x]]=x,w[a[y]]=y;
change(1,n,1,a[x]);
change(1,n,1,a[y]);
}
else {
d1=ask1(x,y,1,n,1),d2=ask2(x,y,1,n,1);
if (d1-d2==y-x)
printf("YES\n");
else printf("NO\n");
}
}
return 0;
}