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;
}