我们发现对于排列(A)中,一组(i<j,a_i<a_j),那么我们不可能通过交换把(a_j)换到(a_i)前面
但是一组(i<j,a_i>a_j),我们却可以通过交换使得(a_j)更靠前,也就是我们在(A)中的交换只能消除一些逆序对,而不能产生新的逆序对
于是我们想要得到排列(B),必须使得(B)中的任意一个逆序对在(A)中也是逆序的,否则就不可能通过交换使得(A)变成(B);即一旦(B)中一个逆序对在(A)中是顺序的,我们就输出(NO)
之后就开始智商下降了,强行莫队+值域分块
我们对于一个数(i),我们设其在(A)中的出现位置为(posa_i),在(B)中出现的位置为(posb_i),那么我们就把((posa_i,posb_i))视为一个点对,将(A)序列中的([1,posa_i])的数都加入值域块,点权为(1);([1,posb_i])的数也都加入值域块,点权为(-1),如果这个时候有一个大于(i)的点点权为(-1),那么就说明这个点在(B)中和(i)形成了逆序对,但是在(A)中却没有。所有点的加入可以通过莫队保证复杂度。
复杂度是(O(nsqrt{n})),成功位列uoj倒数第五
代码
#include<bits/stdc++.h>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=1e5+5;
struct Point{int x,y,rk;}p[maxn];
int a[maxn],b[maxn],id[maxn],B,n,cnt,L[500],R[500];
int tax[maxn],tag[500];
inline void add(int x,int v) {
tax[x]+=v;
if(v==1&&tax[x]==0) tag[id[x]]--;
if(v==-1&&tax[x]==-1) tag[id[x]]++;
}
inline int find() {
for(re int i=cnt;i;--i) {
if(!tag[i]) continue;
for(re int j=R[i];j>=L[i];--j)
if(tax[j]==-1) return j;
}
return 0;
}
inline int cmp(Point A,Point B) {return id[A.x]==id[B.x]?A.y<B.y:A.x<B.x;}
int main() {
n=read();B=std::sqrt(std::ceil(n));
for(re int i=1;i<=n;i++) p[i].rk=i;
for(re int i=1;i<=n;i++) a[i]=read(),p[a[i]].x=i;
for(re int i=1;i<=n;i++) b[i]=read(),p[b[i]].y=i;
for(re int l=1,r;l<=n;l=r+1) {
r=min(n,l+B-1);++cnt;L[cnt]=l,R[cnt]=r;
for(re int j=l;j<=r;++j) id[j]=cnt;
}
std::sort(p+1,p+n+1,cmp);
int l=0,r=0;
for(re int i=1;i<=n;i++) {
while(l<p[i].x) add(a[++l],1);
while(l>p[i].x) add(a[l--],-1);
while(r<p[i].y) add(b[++r],-1);
while(r>p[i].y) add(b[r--],1);
if(find()>p[i].rk) return puts("NO"),0;
}
puts("YES");
return 0;
}
正解其实非常简单,我们维护一个树状数组,按照(i)从小到大把((posa_i,posb_i))插入树状数组;插入之前查一下之前插入的(j)中满足(posa_j<posa_i)的(posb_j)的最大值,如果这个最大值大于(posb_i),那么就说明了(A)中的一个顺序对在(B)中变成了逆序对,直接输出(NO)即可。