CF1187D题解
更新
2024/8/19 删去了不严谨的异或判重,感谢 @Ether13 大佬的提醒详见此帖
题目大意:
给定长为
感性理解:
首先序列
其次排序的过程,考虑冒泡排序,对每个无序的长度为
从前往后考虑依次将
此时我们考虑排序过程是相邻逆序对交换,若出现在
简化为
- 判重(异或判重,也可用随机值判重)
- 找
j 到i 中的最小值 - 标记已排好序的数
暴力 70pts 代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int t,n,ah,bh;
int a[N],b[N],vis[N];
bool pan(){
if(ah!=bh) return 0;//异或判重(不严谨,建议不要使用)
int ma=n;
for(int i=1;i<=n;i++){
int cnt=1;
for(int j=1;j<=n;j++){//暴力搜索最小值
if(vis[j]) continue;
if(a[j]==b[i]&&cnt){
vis[j]=1;//标记排好序的数
break;
}
if(a[j]<b[i]) return 0;
}
}
return 1;
}
int main(){
scanf("%d",&t);
while(t--){
ah=bh=0;
scanf("%d",&n);
for(int i=1;i<=n;i++) vis[i]=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
ah^=a[i];
}
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
bh^=b[i];
}
if(pan()) printf("YES\n");
else printf("NO\n");
}
}
优化
我们考虑打标记对取最小值的影响,可以将已排好序的数赋值为
现在问题简化成单点修改,前缀查询最小值,我们发现一个事情——这很线段树。
用线段树进行优化。
AC 代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5,inf=1e9+5;
int q,n,ha,hb,suma,sumb;
int a[N],b[N],v[N],t[4*N],c[N],tota[N],totb[N];
map<int,map<int,int> >s;
void build(int k,int l,int r){
if(l==r){
t[k]=a[l];
return;
}
int mid=(l+r)>>1;
build(2*k,l,mid);
build(2*k+1,mid+1,r);
t[k]=min(t[2*k],t[2*k+1]);
return;
}
void dotchange(int k,int l,int r,int x){
if(l==r&&l==x){
t[k]=inf;
return;
}
int mid=(l+r)>>1;
if(x<=mid) dotchange(2*k,l,mid,x);
if(x>mid) dotchange(2*k+1,mid+1,r,x);
t[k]=min(t[2*k],t[2*k+1]);
}
int longquery(int k,int l,int r,int x,int y){
if(x<=l&&r<=y){
return t[k];
}
int res=inf;
int mid=(l+r)>>1;
if(x<=mid) res=min(longquery(2*k,l,mid,x,y),res);
if(y>mid) res=min(longquery(2*k+1,mid+1,r,x,y),res);
return res;
}
bool pan(){
for(int i=1;i<=n;i++) if(tota[i]) return 0;
// if(ha!=hb||suma!=sumb) return 0;再次强调,异或判重和前缀和判重都不严谨
for(int i=1;i<=4*n+1;i++) t[i]=inf;//注意多测
build(1,1,n);
for(int i=1;i<=n;i++){
++v[b[i]];
int z=longquery(1,1,n,1,max(s[b[i]][v[b[i]]]-1,1));//前缀查询最小值
if(z<b[i]) return 0;
dotchange(1,1,n,max(s[b[i]][v[b[i]]],1));//单点修改 inf
}
return 1;
}
int main(){
scanf("%d",&q);
while(q--){
// ha=0,hb=0,suma=0,sumb=0;
scanf("%d",&n);
for(int i=1;i<=n;i++) v[i]=0,c[i]=0,tota[i]=0;
s.clear();
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
s[a[i]][++c[a[i]]]=i;//依次记录每个 a[i] 出现的位置
// ha^=a[i];
// suma+=a[i];
tota[a[i]]++;
}
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
// hb^=b[i];
// sumb+=b[i];
tota[b[i]]--;
}
if(pan()) printf("YES\n");
else printf("NO\n");
}
}