Chords

阅前注意:本文下标均从 0 开始,代码通常遵循“左闭右开”规则。

题意简述

在圆上有 条弦(不会共用一个端点),判断是否有任意两条弦相交。

思路简析

圆上问题不好想,不如转换成 OIer 们喜闻乐见的序列问题。将圆从 号点与 号点间断开,拉直,让它变成区间问题。

例如:
1706595909043.png
可以转化成:
1706596374816.png

容易发现,当两条弦相交时,当且仅当他们所对应的区间不存在包含关系但有重叠(形式化的,对于区间 ,应满足 )。此时可以通过两层循环枚举两个区间,实现 的暴力做法。

如何优化到题目可接受的复杂度内?我使用了树状数组

具体来说:

  1. 维护一个树状数组,初始全部为
  2. 按照左端点从小到大的顺序枚举区间
    1. 先判断区间两端的单点值是否相同,若不相同则说明出现交点
    2. 否则将该区间内元素

正确性证明

证明可能有不严谨,还请谅解,理解万岁。

假如数组内已经存入了区间 。那么对于满足 的任意 ,其树状数组单点查询的结果 。加入第二个区间 时,按左端点排序使其必定满足 。那么 的关系有以下几种:

  • ,此时 ,区间无重合。
  • ,此时 ,两个区间满足题目要求。
  • ,此时 ,两个区间是包含关系,不会产生交点。

代码

#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;
}
posted @ 2024-01-30 15:30  MrPython  阅读(28)  评论(0)    收藏  举报  来源