「NOI2022 D2T2 氣泡排序」題解

ft61發表於2024-09-01

題意

uoj768

構造長為 \(n\) 的序列 \(a\),滿足 \(m\) 條限制:\(\min_{j=L_i}^{R_i}\{a_j\}=V_i\),要求逆序對數最少

題解

21pts 暴力

先進行一些觀察:

  1. 逆序對只關心相對大小,所以 \(\forall a_j\) 必然 \(\in\{V_i\}\),可以完全離散化
  2. 經典結論:若 \(i<j,a_i>a_j\) 且交換後合法,則交換後更優

A

\(V_i=1\) 的限制等價於區間覆蓋 \(1\)\(V_i=0\) 的限制等價於區間中存在 \(0\)

類似一個普及貪心題:按 \(L\) 從大到小考慮,區間中有 \(0\) 就跳過,沒有就令第一個空位為 \(0\)(若第一個空位為 \(1\),則一定可以與區間中的 \(0\) 交換)

此時所有限制都滿足了,剩下的位置可以隨意填 \(0,1\),一定是字首 \(0\) 字尾 \(1\),列舉分界點即可

B

相當於一些位置給定,其餘位置隨意

根據觀察 2,空位是有序的,內部沒有逆序對

一個自然的想法是單獨考慮每個空位,令其與給定位置的逆序對數最少。事實上這樣就能滿足空位有序(反證,若有空位 \(i<j,a_i>a_j\),則“將 \(a_i\) 變成 \(a_j\)”和“將 \(a_j\) 變成 \(a_i\)”的增量互為相反數,一定有一個 \(\le0\)

也可以用決策單調性證明:
從前到後依次考慮每個空位,設 \(f[x]\) 表示當前位置填 \(x\) 與已經確定位置構成的逆序對數
空位會填 \(a_j=\text{argmin}\{f[x]\}\),令 \(f[1\sim a_j-1]+1\)
給定位置會令 \(f[1\sim a_j-1]+1,f[a_j+1\sim m]-1\)\(\text{argmin}\) 是單增的(若有 \(x<y,f[x]\ge f[y]\) 則修改後仍滿足)

C

相當於限制是獨立的

根據觀察 2,每個限制一定是在 \(L_i\) 處填 \(V_i\)。問題轉化為一些位置給定,其餘位置滿足 \(a_j\ge b_j\)

根據 B 猜測 \(a_j=\text{argmin}_{x\ge b_j}\{f[x]\}\)

證明類似。首先不論填什麼都會使 \(f[1\sim b_j-1]+1\),其次現在 \(b_j\sim a_j-1\) 劣於 \(a_j\),之後一定不會變優,所以給 \(f[b_j\sim a_j-1]+1\) 沒有影響

std

與 C 相比限制有重疊

\(V_i\) 大的可以替代掉小的。按 \(V_i\) 從大到小排序,同一 \(V_i\) 做 A,即可轉化成 C

時間複雜度 \(O(n\log n)\),空間複雜度 \(O(n)\)

#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std; using namespace __gnu_pbds; using namespace __gnu_cxx;
#define For(i,x,y,...) for(int i=x,##__VA_ARGS__;i<=(y);++i)
#define rFor(i,x,y,...) for(int i=x,##__VA_ARGS__;i>=(y);--i)
#define Rep(i,x,y,...) for(int i=x,##__VA_ARGS__;i<(y);++i)
#define pb emplace_back
#define sz(a) int((a).size())
#define all(a) (a).begin(),(a).end()
#define fi first
#define se second
#define mkp make_pair
typedef long long LL; typedef vector<int> Vi; typedef pair<int,int> Pii;
auto ckmax=[](auto &x,auto y) { return x<y ? x=y,true : false; };
auto ckmin=[](auto &x,auto y) { return y<x ? x=y,true : false; };
sfmt19937 mt(chrono::steady_clock::now().time_since_epoch().count());
int rnd(int l,int r) { return uniform_int_distribution<>(l,r)(mt); }
#define getchar() getchar_unlocked()
struct IO {
	template<typename T>IO& operator >> (T &x) {
		x=0;bool f=0;char c;while(!isdigit(c=getchar()))f|=c=='-';
		do x=x*10+c-48;while(isdigit(c=getchar()));if(f)x=-x;
		return *this;
	}
} io;
#define cin io
template<typename T=int>T read() { T x; cin>>x; return x; }

const int mod = 998244353;
struct mint {
	int x; mint(int x=0):x(x<0?x+mod:x<mod?x:x-mod){}
	mint(LL y) { y%=mod, x=y<0?y+mod:y; }
	mint& operator += (const mint &y) { x=x+y.x<mod?x+y.x:x+y.x-mod; return *this; }
	mint& operator -= (const mint &y) { x=x<y.x?x-y.x+mod:x-y.x; return *this; }
	mint& operator *= (const mint &y) { x=1ll*x*y.x%mod; return *this; }
	friend mint operator + (mint x,const mint &y) { return x+=y; }
	friend mint operator - (mint x,const mint &y) { return x-=y; }
	friend mint operator * (mint x,const mint &y) { return x*=y; }
};	mint Pow(mint x,LL y=mod-2) { mint z(1);for(;y;y>>=1,x*=x)if(y&1)z*=x;return z; }

const int N = 1e6+5;
int n,m,o[N],L[N],R[N],V[N],a[N],b[N];
vector<Pii> q[N];

int fa[N];
int find(int x) { return fa[x]==x ? x : fa[x]=find(fa[x]); }

#define ls (u<<1)
#define rs (u<<1|1)
#define mid (l+r>>1)
struct { int add; Pii mn; } t[N*4];
Pii operator + (const Pii &x,const Pii &y) { return x.fi<=y.fi ? x : y; }
void bld(int u=1,int l=1,int r=o[0]) {
	t[u] = {0,{0,l}};
	if( l == r ) return;
	bld(ls,l,mid), bld(rs,mid+1,r);
}
void add(int ql,int qr,int x,int u=1,int l=1,int r=o[0]) {
	if( qr < l || r < ql ) return;
	if( ql <= l && r <= qr ) { t[u].add += x, t[u].mn.fi += x; return; }
	add(ql,qr,x,ls,l,mid), add(ql,qr,x,rs,mid+1,r);
	t[u].mn = t[ls].mn + t[rs].mn, t[u].mn.fi += t[u].add;
}
Pii argmin(int ql,int u=1,int l=1,int r=o[0]) {
	if( r < ql ) return {1e9,0};
	if( ql <= l ) return t[u].mn;
	Pii res = argmin(ql,ls,l,mid) + argmin(ql,rs,mid+1,r);
	return res.fi += t[u].add, res;
}

struct {
	int t[N];
	void clr() { memset(t+1,0,sizeof(int)*o[0]); }
	void add(int i) { for(;i;i-=i&-i)++t[i]; }
	int sum(int i) { int res=0; for(;i<=o[0];i+=i&-i)res+=t[i]; return res; }
} ft;

void MAIN() {
	cin>>n>>m; For(i,1,m) cin>>L[i]>>R[i]>>V[i], o[++o[0]] = V[i];
	sort(o+1,o+o[0]+1), o[0] = unique(o+1,o+o[0]+1)-o-1;
	For(i,1,m) q[lower_bound(o+1,o+o[0]+1,V[i])-o].pb(L[i],R[i]);
	iota(fa+1,fa+n+2,1);
	rFor(i,o[0],1) {
		sort(all(q[i]),greater<>());
		int lst = n+1;
		for(auto [l,r] : q[i]) if( r < lst ) {
			int p = find(l);
			if( p > r ) { cout<<"-1\n"; return; }
			a[p] = i, lst = p;
		}
		for(auto [l,r] : q[i])
			for(int p = find(l); p <= r; p = find(p)) b[p] = i, fa[p] = p+1;
	}
	// cerr<<"a: "; For(i,1,n) cerr<<a[i]<<" "; cerr<<'\n';
	// cerr<<"b: "; For(i,1,n) cerr<<b[i]<<" "; cerr<<'\n';
	bld();
	For(i,1,n) if( a[i] ) add(a[i]+1,o[0],1);
	For(i,1,n) {
		if( a[i] ) add(a[i]+1,o[0],-1);
		else a[i] = argmin(b[i]).se;
		add(1,a[i]-1,1);
	}
	// cerr<<"a: "; For(i,1,n) cerr<<a[i]<<" "; cerr<<'\n';
	LL ans = 0;
	For(i,1,n) ans += ft.sum(a[i]+1), ft.add(a[i]);
	cout<<ans<<'\n';
} signed main() {
#ifdef FT
	freopen("in","r",stdin); freopen("out","w",stdout);
#endif
	ios::sync_with_stdio(0);
	int lft=read(); while( lft-- ) {
		MAIN();
		ft.clr();
		For(i,1,o[0]) q[i].clear();
		memset(a+1,0,sizeof(int)*n), memset(b+1,0,sizeof(int)*n);
		o[0] = 0;
	}
	return 0;
}

相關文章