Chords
阅前注意:本文下标均从 0 开始,代码通常遵循“左闭右开”规则。
题意简述
在圆上有 条弦(不会共用一个端点),判断是否有任意两条弦相交。
思路简析
圆上问题不好想,不如转换成 OIer 们喜闻乐见的序列问题。将圆从 号点与 号点间断开,拉直,让它变成区间问题。
例如:
可以转化成:
容易发现,当两条弦相交时,当且仅当他们所对应的区间不存在包含关系但有重叠(形式化的,对于区间 与 ,应满足 或 )。此时可以通过两层循环枚举两个区间,实现 的暴力做法。
如何优化到题目可接受的复杂度内?我使用了树状数组。
具体来说:
- 维护一个树状数组,初始全部为
- 按照左端点从小到大的顺序枚举区间
- 先判断区间两端的单点值是否相同,若不相同则说明出现交点
- 否则将该区间内元素 。
正确性证明
证明可能有不严谨,还请谅解,理解万岁。
假如数组内已经存入了区间 。那么对于满足 的任意 ,其树状数组单点查询的结果 。加入第二个区间 时,按左端点排序使其必定满足 。那么 与 的关系有以下几种:
- ,此时 ,区间无重合。
- ,此时 ,两个区间满足题目要求。
- ,此时 ,两个区间是包含关系,不会产生交点。
代码
#include<bits/extc++.h>
using namespace std;
namespace pbds=__gnu_pbds;
using ui=unsigned int;
using uli=unsigned long long int;
using li=long long int;
// 树状数组类,随用随拷贝
class FenwickTree{
vector<int> tree;
int prefix_sum(size_t p) const{
int res=0;
for (--p;p<tree.size();p=(p&p+1)-1) res+=tree[p];
return res;
}
public:
FenwickTree(size_t n):tree(n){}
void add_point(size_t p,int const& val){
for (;p<tree.size();p|=p+1) tree[p]+=val;
}
int get_sum(size_t l,size_t r) const{
return prefix_sum(r)-prefix_sum(l);
}
size_t size(void) const{return tree.size();}
};
class DifferenceFenwick{
FenwickTree tree;
public:
DifferenceFenwick(size_t n):tree(n){}
friend istream& operator>>(istream& in,DifferenceFenwick& t){
for (size_t i=0;i<t.size();i++){
int x;
in>>x;
t.add_point(i,x);
}
return in;
}
void add_interval(size_t l,size_t r,int const& val){
tree.add_point(l,val),tree.add_point(r,-val);
}
void add_point(size_t p,int const& val){add_interval(p,p+1,val);}
int get_val(size_t p){return tree.get_sum(0,p+1);}
size_t size(void){return tree.size();}
};
int main(void){
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
size_t n;
cin>>n;
vector<pair<ui,ui>> a(n);
for (pair<ui,ui>& i:a){
cin>>i.first>>i.second;
if (--i.first > --i.second) swap(i.first,i.second); // 下标从 0 开始,因此全部 -1;检查大小关系,满足 左端点<右端点
}
sort(a.begin(),a.end()); // 按照左端点排序。std::pair 已经重载了相关运算符。
DifferenceFenwick d(n*2); // 步骤 1
for (pair<ui,ui> i:a){ // 步骤 2
if (d.get_val(i.first)!=d.get_val(i.second)) cout<<"Yes",exit(0); // 2.1
d.add_interval(i.first,i.second+1,1); // 2.2
}
cout<<"No";
return 0;
}