专栏文章

Solution P14062 | Well I'm still NOOB

P14062题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@minsq444
此快照首次捕获于
2025/12/02 07:44
3 个月前
此快照最后确认于
2025/12/02 07:44
3 个月前
查看原文
注意到进行 kk 级排序的时候,已经进行了 2k,3k,4k,2k,3k,4k,\cdots 级排序。
所以取出下标模 kkxx 的一个序列(xx[0,k)[0,k) 中的任意整数)后,这个序列不应出现距离 2\ge 2 的逆序对。
也就是说,题目中的排序操作,可以改成跑一轮冒泡排序(检查相邻两项,若是逆序对则交换)!
重新模拟整个过程,不难发现它就等价于每次交换最远的一对逆序对,而且相同距离的逆序对互不影响。

现在引入拆分值域的思想。枚举一个 ii,将所有 i\le i 的值赋为 00,否则赋为 11,求出将 0101 序列排序需要的排序轮数 FiF_i。答案即为 maxi=1n1Fi\max \limits_{i=1}^{n-1} F_i
操作相当于每次将最右边的 11 和最左边的 00 交换,直到排好序。
考虑在序列上找到一个分界线,分界线左边的 11 的数量等于右边的 00 的数量,然后这些逆序的 (1,0)(1,0) 对将会依次完成交换。
由于最终状态,不难发现分界线就在 iii+1i+1 之间。
那么求出 i+1i+1 右边下标最大的 00ii 左边下标最大的 11 的下标之差(因为它们是最后一对交换的 (1,0)(1,0)),这样就可以算出 FiF_i
ii 扫描线。后面的维护方法不再赘述。
时间复杂度 O(n)\mathcal{O}(n)(这里偷懒写了并查集,多了 α(n)\alpha(n))。
CPP
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll> P;
#define fi first
#define se second
#define mkp make_pair
#define pb emplace_back
#define popcnt __builtin_popcountll
const ll mod = 998244353;
inline ll read(){
	ll x=0, f=1; char ch=getchar();
	while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
	while(ch>='0' && ch<='9') x=x*10+ch-'0', ch=getchar();
	return x*f;
}
inline int lg2(int x){ return 31^__builtin_clz(x); }
inline ll lg2(ll x){ return 63^__builtin_clzll(x); }
inline void addmod(int &x){ if(x >= mod) x -= mod; }
inline void addmod(ll &x){ if(x >= mod) x -= mod; }
inline ll qpow(ll a,ll b){
	ll ans=1, base=a;
	while(b){
		if(b&1) ans=ans*base%mod;
		base=base*base%mod; b>>=1;
	}
	return ans;
}
inline ll INV(ll x){ return qpow(x, mod-2); };
int n,p[4000005],iv[4000005],L[4000005],R[4000005];
int fa[4000005];
inline int find(int x){
	if(x!=fa[x]) fa[x]=find(fa[x]);
	return fa[x];
}
inline void merge(int x,int y,int p){
	x=find(x),y=find(y);
	if(!p){
		if(x>y) fa[x]=y; else fa[y]=x;
	}else{
		if(x<y) fa[x]=y; else fa[y]=x;
	}
}
void procedure(){
	n=read();
	for(int i=1;i<=n;i++) iv[p[i]=read()]=fa[i]=i;
	for(int i=1;i<n;i++){
		int pos=iv[i];
		if(pos>1 && p[pos-1]<=i) merge(pos-1,pos,0);
		if(pos<n && p[pos+1]<=i) merge(pos,pos+1,0);
		if(p[i]<=i){
			L[i]=i-find(i)+1;
			if(find(i)==1) L[i]=n;
		}else L[i]=0;
	}
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=n-1;i>=1;i--){
		int pos=iv[i+1];
		if(pos>1 && p[pos-1]>i) merge(pos-1,pos,1);
		if(pos<n && p[pos+1]>i) merge(pos,pos+1,1);
		if(p[i+1]>i){
			R[i]=find(i+1)+1-i;
			if(find(i+1)==n) R[i]=n;
		}
		else R[i]=1;
	}
	int ans=n;
	for(int i=1;i<n;i++){
		ans=min(ans,L[i]+R[i]);
	}
	printf("%d\n",n-ans);
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...