https://ac.nowcoder.com/acm/contest/3007/H
神思路。。。
倘若两片云能够相遇,那么把他们映射到y=-x上的线段相交
为什么?
第一象限往下,第三象限往右他们最先相交的那两个点的初始位置和交点位置构成一个等腰直角三角形
如果没有这样的等腰直角三角形则不能相交
y=-x与该三角形底边垂直
所以两点初始位置向y=-x作垂线,垂足重合
如何将矩形映射到y=-x上?
将左上角和右上角这两个点向y=-x作垂线,根据等腰直角三角形推一推可得垂足的纵坐标为(y-x)/2
除以2会产生小数,不用除效果是一样的
然后问题转化成有一些A类线段和一些B类线段,求AB相交的线段对数
扫描线
将线段的端点从小到大排序
分别记录当前A类线段、B类线段个数
碰到线段左端点,对应类别线段个数+1,并累加当前另一种类别线段的个数
即每次累计左端点在该线段之前并与该线段相交的线段个数
碰到线段右端点,对应类别线段个数-1
#include<cstdio> #include<algorithm> using namespace std; #define N 100001 struct node { int pos,lr,ty; node(int pos_=0,int lr_=0,int ty_=0) : pos(pos_),lr(lr_),ty(ty_) {} }e[N<<2]; int cnt[N]; bool cmp(node p,node q) { if(p.pos==q.pos && p.lr==q.lr) return p.ty<q.ty; if(p.pos==q.pos) return p.lr>q.lr; return p.pos<q.pos; } int main() { int n,m,xa,ya,xb,yb,tot=0; scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) { scanf("%d%d%d%d",&xa,&ya,&xb,&yb); e[++tot]=node(yb-xb,1,0); e[++tot]=node(ya-xa,-1,0); } for(int i=1;i<=m;++i) { scanf("%d%d%d%d",&xa,&ya,&xb,&yb); e[++tot]=node(yb-xb,1,1); e[++tot]=node(ya-xa,-1,1); } sort(e+1,e+tot+1,cmp); long long ans=0; for(int i=1;i<=tot;++i) { cnt[e[i].ty]+=e[i].lr; if(e[i].lr==1) ans+=cnt[e[i].ty^1]; } printf("%lld",ans); }