题意:日本的东西海岸分别有n,m个城市,现在有k条线路将东西岸的城市连接起来,求有多少个线路交叉点。
思路:这道题的本质是统计逆序对,我们按照东岸编号先排个序,那么假设当前这条线路是东-西是 x - y。
首先考虑东海岸编号互不相同,如果之前的线路中,有西海岸的城市编号大于y,就会产生一个交叉点。因为之前的线路中,东海岸的编号都是小于x,所以必然会交叉。所以我们排好序之后,只看西海岸的编号统计逆序对即可。
再考虑如果同一个东海岸连接不同的西海岸,怎么连都不会产生交叉点。所以让西海岸升序排作为第二关键字,这样就不会产生逆序对。
求逆序对可以倒着统计,第i个元素a[i],先用求以下i位置之后有多少个元素比a[i]小,然后再把a[i]插进去。
tips:这道题的范围是10^6,题目写错了。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <algorithm>
using namespace std;
#define LL long long
#define rep(i,j,k) for(int i = j; i <= k; i++ )
#define Rrep(i,j,k) for(int i = j; i >= k; i-- )
#define Clean(x,y) memset(x,y,sizeof(x))
const int maxn =1000000;
int T,n,m,k;
LL sum[maxn+10];
int lowbit(int x)
{
return x&(-x);
}
void add(int x)
{
while( x <= maxn )
{
sum[x]++;
x+=lowbit(x);
}
}
int query(int x)
{
int ans = 0;
while(x)
{
ans+=sum[x];
x-=lowbit(x);
}
return ans;
}
struct node
{
int l,r;
}a[maxn+10];
bool cmp(node a,node b)
{
if ( a.l == b.l ) return a.r < b.r;
return a.l < b.l;
}
int main()
{
cin>>T;
int kase = 0;
while(T--)
{
memset(sum,0,sizeof(sum));
cin>>n>>m>>k;
rep(i,1,k) scanf("%d %d",&a[i].l,&a[i].r);
sort(a+1,a+1+k,cmp);
LL ans = 0;
Rrep(i,k,1)
{
ans+=query(a[i].r-1);
add(a[i].r);
}
printf("Test case %d: %I64d\n",++kase,ans);
}
return 0;
}