jzoj6191-[NOI2019模拟2019.5.31]Exchange【线段树】

正题


题目大意

一个序列,给定若干个区间 [ l . . r ] [l..r] [l..r]
l ∼ r l\sim r lr任意一个位置出发,见到比手中大的数字就交换,到 r r r求最小的交换次数
(注意,并不是真的交换)


解题思路

首先算出每个点的后继(在它后面第一个比他大的数) n e x t i next_i nexti,然后由 n e x t i next_i nexti连接像 i i i的化就会变成一个森林。

我们可以发现两个性质

  1. 任意一个子树中的序号都是连续的
  2. 答案为只由 l ∼ r l\sim r lr的区间内的节点构成的树深度最大的点

T T T为原来的森林

为了方便讲解我们建立一个新的森林叫做 T ′ T' T,首先一个右指针 k k k表示目前 1 ∼ k 1\sim k 1k的点都在 T ′ T' T

我们将询问的有节点从小到大排序,然后每次将 k k k移动到 r r r,中途要加入点(加入一个点会导致 T ′ T' T中改点在 T T T中的·1子树所有深度加一)

然后询问 l ∼ r l\sim r lr的最小深度就好了


c o d e code code

#include
#include
#include
#define Tsize(x) (t[x].r-t[x].l+1)
using namespace std;
const int N=2e6;
struct Tree_node{
	int l,r,w,lazy;
};
struct Q_node{
	int l,r,id;
}q[N];
struct Edge_node{
	int to,next;
}a[N];
int n,next[N],val[N],tot,m;
int ls[N],cnt,ans[N],begin[N];
struct Line_Cut_Tree{
	Tree_node t[N*4];
	void build(int x,int l,int r){
		t[x].l=l;t[x].r=r;
		if(l==r) return;
		int mid=(l+r)/2;
		build(x*2,l,mid);
		build(x*2+1,mid+1,r);
	}
	void downdata(int x)
	{
		if(!t[x].lazy) return;
		t[x*2].w+=t[x].lazy;
		t[x*2+1].w+=t[x].lazy;
		t[x*2].lazy+=t[x].lazy;
		t[x*2+1].lazy+=t[x].lazy;
		t[x].lazy=0;
	}
	void change(int x,int l,int r){
		if(t[x].l==l&&t[x].r==r)
		{
			t[x].lazy++;t[x].w++;
			return;
		}
		downdata(x);
		if(r<=t[x*2].r) change(x*2,l,r);
		else if(l>t[x*2].r) change(x*2+1,l,r);
		else change(x*2,l,t[x*2].r),change(x*2+1,t[x*2+1].l,r);
		t[x].w=max(t[x*2].w,t[x*2+1].w);
	}
	int ask(int x,int l,int r){
		if(t[x].l==l&&t[x].r==r)
			return t[x].w;
		downdata(x);
		if(r<=t[x*2].r) return ask(x*2,l,r);
		if(l>t[x*2].r) return ask(x*2+1,l,r);
		return max(ask(x*2,l,t[x*2].r),ask(x*2+1,t[x*2+1].l,r));
		t[x].w=max(t[x*2].w,t[x*2+1].w);
	}
}Tree;
bool cmp(Q_node x,Q_node y)
{return x.r<y.r;}
int main()
{
	//freopen("exchange.in","r",stdin);
	//freopen("exchange.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	  scanf("%d",&val[i]);
	val[++n]=2147483647;
	for(int i=n-1;i>=1;i--){
		next[i]=i+1;
		while(val[i]>=val[next[i]])
		  next[i]=next[next[i]];
		begin[i]=i;
	}
	begin[n]=n;
	for(int i=1;i<=n;i++)
		begin[next[i]]=min(begin[next[i]],begin[i]);
	for(int i=1;i<=n;i++)
	  printf("%d ",next[i]);
	Tree.build(1,1,n);
    for(int i=1;i<=m;i++)
    	scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
    sort(q+1,q+1+m,cmp);int right=0;
    for(int i=1;i<=m;i++){
    	while(right<q[i].r)
    		right++,Tree.change(1,begin[right],right);
    	ans[q[i].id]=Tree.ask(1,q[i].l,q[i].r);
    }
    for(int i=1;i<=m;i++)
      printf("%d\n",ans[i]);
}

你可能感兴趣的:(数据结构)