[luoguT30208]太极剑

[luoguT30208]太极剑

试题描述

在学习太极之后,Bob 要求 Alice 教他太极剑。Alice 告诉他首先需要通过一项基本剑术测试。测试要求 Bob 尽可能快地切断 (n) 根绳子。

所有绳子的端点两两不同,所以共有 (2n) 个端点。这些端点被捆在一个圆上,等距离分布。我们把这些端点按顺时针方向编号为 (1)(2n)

Bob 每次切割的轨迹是一条直线,可以将所有与这条直线相交的绳子切断,他想知道至少多少次可以切断所有的绳子。

输入

第一行一个整数 (n(1 leq n leq 2 imes 10^5)),表示绳子的个数。

接下来 (n) 行,每行两个整数 (a_i, b_i(1 leq a_i, b_i leq 2n, a_i ot= b_i)),表示第 (i) 根绳子的两个端点的编号。

输出

一行一个整数,表示答案。

输入示例1

2
1 2
3 4

输出示例1

1

输入示例2

3
1 2
3 4
5 6

输出示例2

2

输入示例3

3
1 3
2 4
5 6

输出示例3

1

数据规模及约定

见“输入

题解

首先这个问题等价于“在圆周上选择尽量少的偶数个点,使得相邻两个点之间没有同色的端点,定义第 (i) 条线段的两个端点 (a_i)(b_i) 的颜色为 (i)”。

首先证明“若存在 (k) 条直线的划分方案,则存在 (2k) 个点的划分方案”。这 (k) 条直线与圆的交点就是一个划分方案,假设相邻两个点之间有同色点,则证明某条线段没有被直线劈开。

然后证明“若存在 (2k) 个点的划分方案,则存在 (k) 调直线的划分方案”。构造方法就是每个点朝往后数 (k) 个点的那个点连一条直线,这样保证每一段弧都“封闭”在直线围成的一个平面区域内。

于是这两个问题就等价了。

后者显然会非常好做。考虑暴力枚举第一个划分点,那么这个圆就展开成了一条链,那么我们的目标就是用尽量少的首尾相接的区间覆盖整个链,使得每个区间内没有同色点对。显然只能贪心跳。

现在我们可以不用枚举所有的起点,考虑圆上最短的线段一定是要被切开的,那么最短的线段所覆盖的那个较短的弧上一定有一个划分点,那么在这个弧上枚举起点即可。假定这个弧上有 (d) 段小弧(就是两个端点之间的弧),那么我们每跳一次,显然能够至少跳 (d) 步,如果跳不了则说明 (d) 步内有同色点,那么这对同色点对应的线段就比我们最初选的那条线段更短了,与“最短”的假设矛盾。由此证明跳至多 (left lfloor frac{n}{d} ight floor) 步即可。复杂度就是线性的了(当然你要预处理每个点能跳到哪里)。

#include <bits/stdc++.h>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}

#define maxn 800010
#define oo 2147483647

int n, col[maxn], nxt[maxn];
bool ncol[maxn];

int cdist(int a, int b) {
	if(a > b) swap(a, b);
	return min(b - a, a - 1 + (n >> 1) - b + 1);
}

int ans;
void solve(int sl, int sr) {
	rep(s, sl, sr) {
		int u = s, cnt = 0;
		while(u < s + (n >> 1)) cnt++, u = nxt[u];
		ans = min(ans, cnt + 1 >> 1);
	}
	return ;
}

int main() {
	n = read() << 2;
	int bestA = -1, bestB = -1;
	rep(i, 1, n >> 2) {
		int a = read(), b = read();
		col[a] = col[b] = col[a+(n>>1)] = col[b+(n>>1)] = i;
		if(bestA < 0 || cdist(a, b) < cdist(bestA, bestB)) bestA = a, bestB = b;
	}
	
	int rgt = 1;
	rep(i, 1, n) {
		while(rgt <= n && !ncol[col[rgt]]) ncol[col[rgt++]] = 1;
		nxt[i] = rgt;
		ncol[col[i]] = 0;
	}
	
	ans = oo;
	if(bestA > bestB) swap(bestA, bestB);
	if(bestB - bestA < bestA - 1 + (n >> 1) - bestB + 1) solve(bestA + 1, bestB);
	else solve(1, bestA), solve(bestB + 1, n >> 1);
	printf("%d
", ans);
	
	return 0;
}
原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/9081469.html