zoukankan      html  css  js  c++  java
  • [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;
    }
    
  • 相关阅读:
    UIViewController生命周期
    NSTImer重复执行任务
    IOS平台汉字转拼音方案
    @properties指针说明
    自定义yum仓库
    man手册、zip备份
    ln 软连接与硬连接
    fdisk分区规划和添加wap交换空间
    window部署ftp服务器
    配置附加权限和LDAP
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/9081469.html
Copyright © 2011-2022 走看看