首页 > 其他分享 >「NOI2022 D2T2 冒泡排序」题解

「NOI2022 D2T2 冒泡排序」题解

时间:2024-09-01 11:49:15浏览次数:10  
标签:空位 return int 题解 冒泡排序 fa NOI2022 sim 逆序

题意

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;
}

标签:空位,return,int,题解,冒泡排序,fa,NOI2022,sim,逆序
From: https://www.cnblogs.com/ft61/p/18357848

相关文章

  • 【题解】Solution Set - NOIP2024模拟赛4
    【题解】SolutionSet-NOIP2024模拟赛4https://www.becoder.com.cn/contest/5501T2沉默乐团https://www.becoder.com.cn/submission/2593352T3深黯「军团」记录一下考场思路:首先对于长度为\(n\)所有排列,按顺序求出她的逆序对数量。然后找到了规律。后面基于此,写出......
  • abc369 题解
    切了A~F,还挺开心(但是如果上一次把G切了的话,我就上青了QAQ比赛链接:https://atcoder.jp/contests/abc369A-369题意:给定正整数\(a,b\)(\(1\lea,b\le100\)),请问有多少个整数\(x\)满足\(a,b,x\)排序后构成等差数列。思路:观察到\(a,b\)范围很小,直接枚举\(x\)即可。......
  • 题解:洛谷 P10996 【MX-J3-T3】Tuple
    原题链接介绍一种(也许是正解的)卡常做法先说总体思路:对于每个三元组\((x,y,z)\),若有一个\(w\)满足\((x,y,w),(x,z,w),(y,z,w)\)均存在,则找到了一个合法的四元组\((x,y,z,w)\)。\(20\\rm{Pts}\)做法如果暴力搜索,在遍历每一个三元组时,每一次都扫描所有的\(w\in[1,N]\)......
  • 题解:洛谷 P10497 [USACO03Open] Lost Cows
    原题链接思路+算法首先,考虑读入到\(a_i\)时,如果要得到此时的最优解(指所有牛的编号不重不漏地覆盖\([1,i]\)的所有编号),对于第\(i\)头奶牛,因为在它前面有\(a_i\)头奶牛的编号小于它,所以第\(i\)头奶牛的编号应当为\(a_i+1\)。如果有一头新的奶牛\(i+1\)加入到这个......
  • 题解:洛谷 P10878 [JRKSJ R9] 在相思树下 III
    原题链接解析在操作一时,最小值如果在最后一位,其无法更新任何数,会被删除;否则不在最后一位时一定会被其右侧更大的数更新。所以在操作一时,最小值一定会被更新掉。同理,在操作二时,最大值一定会被更新掉。由此,操作一决定了答案的下限,操作二决定了答案的上限。所以可以得出贪心策略......
  • LOJ #6089. 小 Y 的背包计数问题 题解
    Description小Y有一个大小为\(n\)的背包,并且小Y有\(n\)种物品。对于第\(i\)种物品,共有\(i\)个可以使用,并且对于每一个\(i\)物品,体积均为\(i\)。求小Y把该背包装满的方案数为多少,答案对于\(23333333\)取模。定义两种不同的方案为:当且仅当至少存在一种物品的......
  • 学校训练赛的一些题解
    第二十一届宁波大学程序设计竞赛(重现赛链接)C游戏开发部的小游戏(C)赛时并没有写出来,果然dp还得多练)将所有石头视为容量为\(n\)的背包,每堆石头的数量即背包中物品的质量,对于\(a_i\leqf_i\leqb_i\),由于\(f_i\)最终取值唯一,可当作分组背包处理。将大小为\(i\)的\(t\)......
  • CCF-CSP 2024 --重塑矩阵1,2c语言题解
     创作想法是因为像我当初大一时候想参加一些比赛但是奈何只学了c和c相关数据结构,但是对于许多竞赛的题目的题解往往都是c++或者其他面向对象的编程语言,让我们难以在c语言基础上入手这些比较复杂的题目。 创造的目的是为了帮助各位同时提高我对c语言编程的理解和锻炼个人......
  • CSP-J 2020 初赛试题解析(第一部分:单项选择题(5-10))
    ......
  • 【秋招笔试】8.30饿了么秋招(算法岗)-三语言题解
    ......