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;
}
}

浙公网安备 33010602011771号