F - Chord Crossing

题目链接:https://atcoder.jp/contests/abc405/tasks/abc405_f

题意:

在一个圆上有2N个点,给定一系列序偶(皆为偶数)作为线段的左右端点并在图上连接

有q次查询,每次查询给出一条线段的左端点和右端点(皆为奇数)

求查询线段和已经连接的线段的交点个数

思路:

不妨把圆看作线段

发现当 查询线段 和 已经连接的线段 相交时,它们两个的区间有重叠

然而两个区间不能是完全包含的关系

离线考虑每一个查询

从1到2N枚举线段左端点

如果i为奇数,那么即为查询线段,否则为连接线段

这样是为了保证线段左端点的有序性,便于后续使用树状数组查询

把已经连接的线段右端点放到树状数组的桶里面

遇到查询线段的端点时,通过树状数组的查询 查询有多少个已连接线段的右端点超过 查询线段的左端点/右端点

由于左端点的有序性,记已连接线段条数为seg,那么此时seg-query(l)即为与查询线段有重叠部分的已连接线段数量

当然遍历到这个查询线段的右端点时也需要将答案加上seg-query(r)

最后需要消除长度长到包含这个查询线段的已连接线段数量,即其左端点在l左边,右端点在r右边

那么我们在遍历到左端点时将答案减去两倍的seg-query(r)即可(因为遍历到右端点时会又统计一遍这样的线段)

int n,m;
int s[maxn];
int lowbit(int x){
    return x&-x;
}
void add(int p,int x){
    while(p<maxn){
        s[p]+=x;p+=lowbit(p);
    }
}
int query(int x){
    int res=0;
    while(x){
        res+=s[x];
        x-=lowbit(x);
    }
    return res;
}
int ans[maxn];
vector<array<int,3>>g[maxn];
void solve(){
    cin>>n>>m;
    rep(i,1,m){
        int x,y;cin>>x>>y;
        g[x].pb({y,0,0});
    }
    int q;cin>>q;
    for(int i=1;i<=q;i++){
        int l,r;cin>>l>>r;
        g[l].pb({l,1,i});
        g[r].pb({r,1,i});
        g[l].pb({r,-2,i});
    }

    int seg=0;
    for(int i=1;i<=2*n;i++){
        if(i&1){
            for(auto[x,y,z]:g[i]){
                ans[z]+=y*(seg-query(x));  
            }
        }else{
            seg+=g[i].size();
            for(auto[x,y,z]:g[i]){
                add(x,1);
            }
        }
    }
    for(int i=1;i<=q;i++){
        cout<<ans[i]<<endl;
    }
}
posted @ 2025-05-12 20:01  Marinaco  阅读(27)  评论(0)    收藏  举报
//雪花飘落效果