IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> D. Subarray Sorting -> 正文阅读

[数据结构与算法]D. Subarray Sorting

题目

D. Subarray Sorting

分析

这道题不该盯着排序,盯着怎么排序,只需要思考序列需要满足
怎么样的条件才能使得 a,b数组通过排序能一一匹配成功
从左到右依次考虑a[i]==b[i]
首先要找到b[i]在a数组当中的位置,如果不存在肯定直接NO
b[i]在a数组当中出现的位置可能有多个,用队列记录下来,
找到b[i]在a种第一次出现的位置x,看b[i]?=min{1,x}
如果b[i]不是最小的,对这段进行排序必然会有更小的阻碍a[x]到i位置
//	(当然了,最小的相对的是未匹配的元素,匹配好了的放在b[i]前面的元素
//	不再参与比较,因为不会再移动了,所以更新匹配元素在线段树中的值,
//设置成正无穷,不会影响后面的最小数排在前面)

以下不通:呜呜呜 

前面i-1个元素已经匹配好了,下一次真正要排序的段落是{i,x} 
为什么要找{1,x}的最小值,而不是{i,x}的最小值呢
线段树查询l,r可以查询到任意区间段的呀
这是因为从左到右依次匹配,1~i-1必然是匹配好了的,且在匹配过程中
我们并没有维护a中元素的位置,只知道把小的元素调到前面,剩余
未匹配的元素必然是后移或是不动的 
而线段树中的{1,x}这些元素,a数组中已经排序好的前i-1个元素未必
全来自原线段树{1,x},也就是,原线段树中的{1,i}中可能有些元素可能
后移到了b[i]以后
即在{1,x}区间内,未匹配的元素都在b[i]后面了,但是一定会在x前面

woc,正难则反,不用寻根究底到底该对哪一段进行排序,之前的排序操作
是否会对后面的序列局势造成影响
总之,无论是匹配b[1]还是b[i],b[i]在a数组中第一次出现的位置为pos
那么a[1~pos]不能出现小于b[i]的尚未匹配的数,否则这个尚未匹配的数, 
在a数组中前于a[pos],且小于a[pos],不管之前进行了怎样的排序,该数
总会阻碍a[pos]归位(由于已经匹配的数不会对后面的数归位造成影响,
于是在线段树维护的a数组中将已经匹配的数设置为无穷大) 

线段树4个小小函数,找bug找到吐血

  • build分l,r ;递归参数有mid
  • modify,query分 t r [ u ] . l tr[u].l tr[u].l,递归参数永远是最初查找范围【l,r】
  • modify,query千万不要else if,因为修改/查询的段落可能只落在左子树,可能只落在右子树,可能二者皆有一部分
  • 函数中的递归参数没有 1 1 1,只有 l l l
#include <bits/stdc++.h>
#include <iostream> 
using namespace std;
const int N=3e5+5;
const int inf=0x3f3f3f3f;
struct node{
	int l,r,minx;
}tr[N*4]; 
int a[N];
int b[N];
queue<int> q[N];
void pushup(int u){//树上节点的编号 
	tr[u].minx=min(tr[u<<1].minx,tr[u<<1|1].minx);
}
void build(int u,int l,int r){//树上节点编号、w数组编号 
	if(l==r){//该段只有一个元素 
		tr[u]={l,r,a[r]};//树上节点维护者w数组中的元素下标噢l肯定等于r啦 
		return ; 
	}
	tr[u]={l,r};//这层递归下来l,r就是以u为根节点的树维护的w中下标范围 
	int mid=(l+r)>>1;//w数组元素分两半给左右子树
	build(u<<1,l,mid);
	build(u<<1|1,mid+1,r); 
	pushup(u); 
}

void modify(int u,int x,int d){
	if(tr[u].l==x&&tr[u].r==x){
		tr[u].minx=d;
		return;
	} 
	int mid=(tr[u].l+tr[u].r)>>1;//是在树中搜w下标x ,build是将w的下标分给左右子树 
	if(x<=mid)modify(u<<1,x,d);
	else modify(u<<1|1,x,d); 
	pushup(u);
}
int query(int u,int l,int r){
	if(tr[u].l>=l&&tr[u].r<=r){//整棵树都在查询范围内 
		return tr[u].minx;
	} 
	int mid=(tr[u].l+tr[u].r)>>1;//是在树中搜w下标x ,build是将w的下标分给左右子树 
	int res=0x3f3f3f3f; 
	if(l<=mid)res=query(u<<1,l,r);
	if(r>mid)res=min(res,query(u<<1|1,l,r)); 
	return res;
}
signed main(){//单点修改,区间最小值 
	ios::sync_with_stdio(false);
	cin.tie(0); 
	int t,n;
	cin>>t;
	while(t--){
		for(int i=1;i<=n;i++){
			while(!q[i].empty())q[i].pop();
		}
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			q[a[i]].push(i);//存储a[i]这个值在a[]中存储的下标 
		}
		for(int i=1;i<=n;i++){
			cin>>b[i];
		}
		build(1,1,n);
		int f=false;
		for(int i=1;i<=n;i++){
			if(q[b[i]].empty()){
				f=true;
				break;//b[i]在a[i]中不存在 
			}
			else{
				int pos=q[b[i]].front();
				q[b[i]].pop();
				if(query(1,1,pos)!=b[i]){
					f=true;
					break;
				}
				else{
					modify(1,pos,inf);
				}
				
			} 

	}
		if(f)cout<<"NO";
		else cout<<"YES";
		if(t)cout<<endl;
	} 
	

	return 0;
}

1、线段树维护a[]的同时,将a[i]的下标插入队列
2、多次样例输入,每次清空队列通过q【a【i】】.pop()
其实a[i]范围和n的范围是一样的,q【i】.pop()这样清空也一样,但还是前者意义上更标准

#include <bits/stdc++.h>
#include <iostream> 
using namespace std;
const int N=3e5+5;
const int inf=0x3f3f3f3f;
struct node{
	int l,r,minx;
}tr[N*4]; 
int a[N];
int b[N];
queue<int> q[N];
void pushup(int u){//树上节点的编号 
	tr[u].minx=min(tr[u<<1].minx,tr[u<<1|1].minx);
}
void build(int u,int l,int r){//树上节点编号、w数组编号 
	if(l==r){//该段只有一个元素 
		tr[u]={l,r,a[r]};//树上节点维护者w数组中的元素下标噢l肯定等于r啦 
		q[a[r]].push(r);
		return ; 
	}
	tr[u]={l,r};//这层递归下来l,r就是以u为根节点的树维护的w中下标范围 
	int mid=(l+r)>>1;//w数组元素分两半给左右子树
	build(u<<1,l,mid);
	build(u<<1|1,mid+1,r); 
	pushup(u); 
}

void modify(int u,int x,int d){
	if(tr[u].l==tr[u].r){
		tr[u].minx=d;
		return;
	} 
	int mid=(tr[u].l+tr[u].r)>>1;//是在树中搜w下标x ,build是将w的下标分给左右子树 
	if(x<=mid)modify(u<<1,x,d);
	else modify(u<<1|1,x,d); 
	pushup(u);
}
int query(int u,int l,int r){
	if(tr[u].l>=l&&tr[u].r<=r){//整棵树都在查询范围内 
		return tr[u].minx;
	} 
	int mid=(tr[u].l+tr[u].r)>>1;//是在树中搜w下标x ,build是将w的下标分给左右子树 
	int res=inf; 
	if(l<=mid)res=min(res,query(u<<1,l,r));
	if(r>mid)res=min(res,query(u<<1|1,l,r)); 
	return res;
}
signed main(){//单点修改,区间最小值 
	ios::sync_with_stdio(false);
	cin.tie(0); 
	int t,n;
	cin>>t;
	while(t--){
		cin>>n;

		for(int i=1;i<=n;i++){
			cin>>a[i];
//			q[a[i]].push(i);//存储a[i]这个值在a[]中存储的下标 
while(!q[a[i]].empty())q[a[i]].pop();
		}
		build(1,1,n);
		for(int i=1;i<=n;i++){
			cin>>b[i];
		}

		int f=false;
		for(int i=1;i<=n;i++){
			if(q[b[i]].empty()){
				f=true;
//				break;//b[i]在a[i]中不存在 
			}
			else{
				int pos=q[b[i]].front();
				q[b[i]].pop();
				int minn=query(1,1,pos);
				if(minn!=b[i]){
					f=true;
//					break;
				}
				else{
					modify(1,pos,inf);
				}
				
			} 
			if(f)break;
		}
		if(f)cout<<"NO";
		else cout<<"YES";
		if(t)cout<<endl;
	} 
	return 0;
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-28 12:04:59  更:2022-04-28 12:06:24 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:37:43-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码