列表

详情


NC223142. 排列

描述

牛牛有  个  的排列 ,因为写下所有的排列太费时间,而且也会写下很多数字,牛牛觉得这过程没有什么意义,经过观察后牛牛发现对于  是  交换第  和  个数字得到的,所以牛牛就先在第一行写下了第一个排列,然后接下来写的全是   和  ,由此完美的解决了排列的存储问题。
但是这样又一个问题诞生了,若牛牛写下所有的数字,他可以轻松的按字典序比较任意两个排列的大小,但现在这种存储方式,却是让这个问题变得十分的复杂,所以现在你的任务就是:将这个  个排列按照字典序从小到大排(若有一样的排列,则让下标较小的出现在前面)得到 ,然后输出 

输入描述

第一行给出两个正整数 ,含义如题

第二行给出  个正整数表示 

接下来  行每行两个正整数,其中第  行两个正整数表示 

 是一个  的排列

输出描述

在一行中输出  个以空格分隔的整数 

示例1

输入:

3 4
3 1 2
1 2
1 3
2 3

输出:

1 3 2 0

说明:

,将  的第一个数字 () 和第二个数字 () 交换,得到 ,类似可以得到 。按字典序排序得到 

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++ 解法, 执行用时: 324ms, 内存消耗: 62216K, 提交时间: 2021-07-10 15:08:57

#include<bits/stdc++.h>
using namespace std;
const int M=1e5+5,P=1e9+7,qwq=10;
int n,m;
int base[M],A[M];
int root[M],tot;
int sum[M*40],Ls[M*40],Rs[M*40],val[M*40];
int ans[M];
void build(int &rt,int L,int R){
	rt=++tot;
	if(L==R){
		sum[rt]=1ll*A[L]*base[L-1]%P;
		val[rt]=A[L];
		return;
	}
	int mid=L+R>>1;
	build(Ls[rt],L,mid);
	build(Rs[rt],mid+1,R);
	sum[rt]=(sum[Ls[rt]]+sum[Rs[rt]])%P;
}
void update(int &rt,int pt,int x,int a,int b,int L,int R){
	rt=++tot;
	sum[rt]=(sum[pt]+a)%P;
	Ls[rt]=Ls[pt],Rs[rt]=Rs[pt];
	if(L==R){
		val[rt]=val[pt]+b;
		return;
	}
	int mid=L+R>>1;
	if(x<=mid)update(Ls[rt],Ls[pt],x,a,b,L,mid);
	else update(Rs[rt],Rs[pt],x,a,b,mid+1,R);
}
bool cmp(int a,int b){
	if(sum[root[a]]==sum[root[b]])return a<b;
	int L=1,R=n;
	int ra=root[a],rb=root[b];
	while(L!=R){
		int mid=L+R>>1;
		if(sum[Ls[ra]]==sum[Ls[rb]]){
			L=mid+1;
			ra=Rs[ra],rb=Rs[rb];
		}else {
			R=mid;
			ra=Ls[ra],rb=Ls[rb];
		}
	}
	return val[ra]<val[rb];
}
int main(){
	scanf("%d %d",&n,&m);
	base[0]=1;
	for(int i=1;i<=n;i++){
		base[i]=1ll*base[i-1]*qwq%P;
		scanf("%d",&A[i]);
	}
	build(root[0],1,n);
	ans[0]=0;
	for(int i=1;i<m;i++){
		int x,y;
		scanf("%d %d",&x,&y);
		update(root[i],root[i-1],x,(1ll*(A[y]-A[x])*base[x-1]%P+P)%P,A[y]-A[x],1,n);	
		update(root[i],root[i],y,(1ll*(A[x]-A[y])*base[y-1]%P+P)%P,A[x]-A[y],1,n);
		swap(A[x],A[y]);
		ans[i]=i;
	}
	sort(ans,ans+m,cmp);
	for(int i=0;i<m;i++)printf("%d ",ans[i]);
	return 0;
}

上一题