东海岸与西海岸分别有N和M个城市,现在修高速公路连接东西海岸的城市,求路线的交点个数。
把每条高速公路记为(x,y), 即东岸的第x个城市与西岸的第y个城市修一条路。当两条路有交点时,满足(x1-x2)*(y1-y2) < 0。如果将每条路按x从小到达排序,若x相同,按y从小到大排序后:
先插入的肯定是X值小的。当插入第一条的时候,比如Y值为M(假设M=10),第二条插入的时候,Y2值为5,那么他们就会有一个交点。如果再插入第三条路线,Y3=3,那线段3和线段1,2分别会有一个交点。。。。因为我们是按照X值递增的顺序插入线段的,那么,是否可以理解为:我们插入的线段Ym 与 之前插入线段 的交点 总个数 ==之前插入线段中Y值比Ym 大 的线段的个数?因为线段Ym的X值肯定比之前插入线段的X值大,而Ym的值又比其小,所以跟之前满足条件的所有线段都有交点。在此例中即线段1和2.
怎么解决这个问题?
因为每次都有一条线段插入,插入线段后求的是比该线段的Yi大的线段的个数 即y值的个数(一个Y值可能对应多条线段,先不考虑此种情况),那么用M减Yi即可。是否能理解为区间和?用树状数组就方便了。同时,因为有线段插入,更改和也较方便。
此时再来考虑Y值对应多条线段的问题。用树状数组后,只需在插入线段的时候,将其Y值加一即可。然后每次插入一条线段,只需求区间和就可以了。
说到底其实就是求逆序数之和。
综上所述:
由于x是从小到大排序的,假设当前我们在处理第k条边,那么第1~k-1条边的x必然是小于(等于时候暂且不讨论)第k条边的 x 的,那么前k-1条边中,与第k条边相交的边的y值必然大于yk的,所以此时我们只需要求出在前k-1条边中有多少条边的y值在区间[yk, M]即可,也就是求yk的逆序数,M为西岸城市个数,即y的最大值。 所以就将问题转化成区间求和的问题,树状数组解决。当两条边的x相同时,我们记这两条边的y值分别为ya,yb(ya<yb),我们先处理(x,ya),再处理(x,yb),原因很明显,因为当x相同时,这两条边是认为没有交点的,若先处理(x,yb),那么下次处理(x,ya)时,(x,ya)就会给(x,yb)增加一个逆序,也就是将这两条边做相交处理了。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
int m;
ll c[3000];
struct node
{
int x;
int y;
}a[1003*1002];
//因为最多有1000个城市,两两之间有路。此处要注意,不然会越界
int cmp(node a,node b)
{
if(a.x==b.x)
return a.y<b.y;
else
return a.x<b.x;
}
int lowbit(int x)
{
return x&(-x);
}
ll sum(int x)
{
ll ret=0;
while(x>0)
{
ret+=c[x];
x-=lowbit(x);
}
return ret;
}
void add(int i,int d)
{
while(i<=m)
{
c[i]+=d;
i+=lowbit(i);
}
}
int main()
{
int t,i,j,k,n,ncase;
cin>>t;
for(ncase=1;ncase<=t;ncase++)
{
cin>>n>>m>>k;
memset(c,0,sizeof(c));
ll ans=0;
for(j=1;j<=k;j++)
{
scanf("%d%d",&a[j].x,&a[j].y);
}
sort(a+1,a+k+1,cmp);//从a[1]开始排序
for(j=1;j<=k;j++)
{
add(a[j].y,1);
ans+=(sum(m)-sum(a[j].y));
}
cout<<"Test case "<<ncase<<": ";
cout<<ans<<endl;
}
return 0;
}