CF1685E The Ultimate LIS Problem 【贪心,AGC】

给定长为 \(2n+1\) 的排列 \(p_1,\cdots,p_{2n+1}\)\(q\) 次操作,每次给出 \(u,v\),表示 \(\text{swap}(p_u,p_v)\) 然后求任意 \(k\in[0,2n]\) 使得 \(p_{k+1},\cdots,p_{2n+1},p_1,\cdots,p_k\) 的 LIS 长度 \(\le n\),需判断无解。

\(n,q\le 10^5\)


怎么判无解阿?就是说所有循环移位的 LIS 长度 \(\ge n+1\)

一个括号序列的结论突然出现:令 \(b_i=[p_i>n+1]-[p_i<n+1]\),其包含 \(n\)\(1\)\(n\)\(-1\)\(1\)\(0\),存在一个循环移位满足所有前缀和 \(\ge 0\)。看看这个循环移位的 LIS,如果不包含 \(n+1\) 则是 \(x\)\(-1\) 接上 \(n+1-x\)\(1\),从而第 \(x\)\(-1\) 在第 \(x\)\(1\) 之前,矛盾了,所以 LIS 必定包含 \(n+1\),之前是 \(x\)\(-1\),之后是 \(n-x\)\(1\),又由前缀和 \(\ge 0\) 的结论知 \(n+1\) 之前也有 \(x\)\(1\),之后也有 \(n-x\)\(-1\)

这个结论启发我们看看 \(n+1\) 在开头和结尾的循环移位的 LIS,可以发现前缀和仍然 \(\ge 0\),所以 LIS 必定包含 \(n+1\),从而两个 LIS 分别为 \(n+1,\cdots,2n+1\)\(1,\cdots,n+1\),也就是说 \(n+1\) 在开头时 \(i\)\(n+1+i\) 都按顺序出现,且 \(n+1+i\)\(i\) 之前(\(i\in[1,n]\))。

事实证明满足这个条件就无解了:对于位置 \(k+1\) 开头的循环移位,位置 \(k\) 之后 \(<n+1\) 的数然后 \(n+1\) 接着位置 \(k\) 之前 \(>n+1\) 的数就是长度 \(\ge n+1\) 的上升序列。

这也推出了求解方法:看看 \(n+1\) 在开头的循环移位,

  • 若有前缀和 \(<0\),就输出任意一个所有前缀和 \(\ge 0\) 的循环移位;
  • \(i\) 不按顺序出现,就输出 \(n+1\) 结尾的循环移位;
  • \(n+1+i\) 不按顺序出现,就输出 \(n+1\) 开头的循环移位。

维护逆排列 \(\text{pos}_i\),然后维护 \(\sum_{i=1}^n[\text{pos}_{i+1}<\text{pos}_i]+[\text{pos}_1<\text{pos}_{n+1}]\)\(\sum_{i=1}^n[\text{pos}_{n+i+1}<\text{pos}_{n+i}]+[\text{pos}_{n+1}<\text{pos}_{2n+1}]\),再用线段树维护前缀和最小值,直接模拟即可,时间复杂度 \(\mathcal O((n+q)\log n)\)

#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 200003, M = 1 << 19;
int n, q, m, p[N], ps[N], seg[M], sm1, sm2;
pii mn[M];
int sgn(int x){return (x > n + 1) - (x <= n);}
void pup(int x){
	seg[x] = seg[x << 1] + seg[x << 1 | 1];
	mn[x] = min(mn[x << 1], MP(mn[x << 1 | 1].fi + seg[x << 1], mn[x << 1 | 1].se));
}
void build(int x = 1, int L = 1, int R = m){
	if(L == R){mn[x] = MP(seg[x] = sgn(p[L]), L); return;}
	int md = L + R >> 1;
	build(x << 1, L, md);
	build(x << 1 | 1, md + 1, R);
	pup(x);
}
void upd(int pos, int x = 1, int L = 1, int R = m){
	if(L == R){mn[x] = MP(seg[x] = sgn(p[L]), L); return;}
	int md = L + R >> 1;
	if(pos <= md) upd(pos, x << 1, L, md);
	else upd(pos, x << 1 | 1, md + 1, R);
	pup(x);
}
int qry(int pos, int x = 1, int L = 1, int R = m){
	if(L == R) return 0;
	int md = L + R >> 1;
	if(pos <= md) return qry(pos, x << 1, L, md);
	return seg[x << 1] + qry(pos, x << 1 | 1, md + 1, R);
}
void work(int x, int val){
	if(x <= n + 1){
		int lst = x == 1 ? n + 1 : x - 1, nxt = x == n + 1 ? 1 : x + 1;
		sm1 -= (ps[x] < ps[lst]) + (ps[nxt] < ps[x]);
	}
	if(x >= n + 1){
		int lst = x == n + 1 ? m : x - 1, nxt = x == m ? n + 1 : x + 1;
		sm2 -= (ps[x] < ps[lst]) + (ps[nxt] < ps[x]);
	}
	ps[x] = val;
	if(x <= n + 1){
		int lst = x == 1 ? n + 1 : x - 1, nxt = x == n + 1 ? 1 : x + 1;
		sm1 += (ps[x] < ps[lst]) + (ps[nxt] < ps[x]);
	}
	if(x >= n + 1){
		int lst = x == n + 1 ? m : x - 1, nxt = x == m ? n + 1 : x + 1;
		sm2 += (ps[x] < ps[lst]) + (ps[nxt] < ps[x]);
	}
}
int main(){
	ios::sync_with_stdio(0);
	cin >> n >> q; m = n * 2 + 1;
	for(int i = 1;i <= m;++ i){cin >> p[i]; ps[p[i]] = i;}
	for(int i = 1;i <= n;++ i){
		sm1 += ps[i + 1] < ps[i];
		sm2 += ps[n + i + 1] < ps[n + i];
	}
	sm1 += ps[1] < ps[n + 1];
	sm2 += ps[n + 1] < ps[m];
	build();
	while(q --){
		int x, y; cin >> x >> y;
		work(p[x], y); work(p[y], x);
		swap(p[x], p[y]); upd(x); upd(y);
		printf("%d\n", mn[1].fi < qry(ps[n + 1]) ? mn[1].se % m : (sm1 != 1 ? ps[n + 1] % m : (sm2 != 1 ? ps[n + 1] - 1 : -1)));
	}
}
posted @ 2022-06-08 21:55  mizu164  阅读(129)  评论(0编辑  收藏  举报