luoguP4005 小 Y 和地铁 搜索 贪心 树状数组

本文详细解析了洛谷P4005小Y和地铁问题,通过搜索+贪心策略解决路线匹配问题。讨论了八种情况下的优化策略,最终采用树状数组进行点数维护,实现O(2^mlogm)的复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

** luoguP4005 小 Y 和地铁**

题目传送门

** 分析**

很有意思的一道搜索+贪心题。
首先在这条路线上只出现过一次的点可以不管他。
所以我们只考虑两两匹配的情况。
一共有以下八种情况
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
暴力所有情况,令 m = n 2 m=\frac{n}{2} m=2n
复杂度 O ( m 8 m ) O(m8^m) O(m8m)
考虑优化,发现图 ( 1 , 2 ) , ( 3 , 4 ) ( 5 , 6 ) ( 7 , 8 ) (1,2),(3,4)(5,6)(7,8) (1,2),(3,4)(5,6)(7,8)形成了环。这意味着某条直线上的闭合曲线与组内的两条线交点个数相同,所以放哪种实际上没差。
复杂度 O ( m 4 m ) O(m4^m) O(m4m)
考虑下面这种情况
在这里插入图片描述
发现他们把直线的后半部分包住了,也就是左端点在后半部分的点和它们俩的交点个数相同。
所以按左端点排序,每次贪心选最优秀的哪一种即可。
复杂度 O ( m 2 m ) O(m2^m) O(m2m)
复杂度其实已经足够优秀,再优化不能从图下手,考虑能不能怼掉那个 m m m
计数的时候发现对于 4 ∗ 4 4*4 44种情况实际上是一个数点问题,用树状数组维护,复杂度 O ( 2 m l o g m ) O(2^mlogm) O(2mlogm)(这复杂度也是够奇葩的)
然后就过啦。

** 代码**

#include<bits/stdc++.h>
const int N = 101;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int tp, n, ans, L[N], R[N], bin[25];
struct Data {
	int l, r;
	bool operator < (Data a) const {return l < a.l;}
}a[N];
struct BIT{
	int t[N];
	void Add(int x, int w) {
		for(;x <= n; x += x&-x) 
			t[x] += w;
	}
	int Que(int x) {
		int r = 0;
		for(;x; x -= x&-x) 
			r += t[x];
		return r;
	}
	int Que(int L, int R) {return Que(R) - Que(L - 1);}
}up, down;
void Dfs(int i, int s) {
	if(i == tp) 
		return void(ans = std::min(ans, s));
	if(s >= ans) return ;
	int r1 = up.Que(a[i].l, a[i].r);
	int r2 = up.Que(a[i].r, n) + down.Que(a[i].l, n);
	up.Add(a[i].r, 1);
	Dfs(i + 1, s + std::min(r1, r2));
	up.Add(a[i].r, -1);
	r1 = down.Que(a[i].l, a[i].r);
	r2 = down.Que(a[i].r, n) + up.Que(a[i].l, n);
	down.Add(a[i].r, 1);
	Dfs(i + 1, s + std::min(r1, r2));
	down.Add(a[i].r, -1);
}
int main() {
	bin[0] = 1; for(int i = 1;i <= 22; ++i) bin[i] = bin[i - 1] << 1;
	for(int T = ri(); T--;) {
		n = ri(); tp = 0;
		for(int i = 1;i <= n; ++i) L[i] = R[i] = 0;
		for(int i = 1;i <= n; ++i) {
			int x = ri();
			if(!L[x]) L[x] = i;
			else R[x] = i;
		}
		for(int i = 1;i <= n; ++i)
			if(L[i] && R[i])
				a[tp].l = L[i], a[tp++].r = R[i];
		std::sort(a, a + tp);
		ans = 1e9; Dfs(0, 0);
		printf("%d\n", ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值