Description
Input
Output
Test case (case number): (number of crossings)
Sample Input
1 3 4 4 1 4 2 3 3 2 3 1
Sample Output
Test case 1: 5
题意:日本岛东海岸与西海岸分别有N和M个城市,现在修高速公路连接东西海岸的城市,求交点个数。
做法:记每条告诉公路为(x,y), 即东岸的第x个城市与西岸的第y个城市修一条路。所以,将每条路按x从小到达排序,若x相同,按y从小到大排序。 然后按排序后的公路用树状数组在线更新,求y的逆序数之 和 即为交点个数。因为只有一条线一个端点高一个端点低才能相交,对x排序已经保证了 x 从低到高(前面的小于后面的),所以y端点如果有前面的大于后面的,则一定相交;同时可能会担心x相同的时候,会不会多+一个逆序对呢。因为在x相同的时候,我们让y从小到大排序,这样保证了不会多加一个逆序对,因为某个y端点没有共享一个x端点的y端点在他前面;
树状数组求逆序对的方法:
逆序对指的是在一个数组种 i < j && ai > aj的组合有几种,做法,for从i,1 - n,先讲a[i]放进树状数组,在求sum[a[i]] 里有几个 比 a[i]小的,因为前面已经在树状数组里的数字都是i 前面的 a[j],这保证了 j < i 这时候前面有几个小于等于a[i]的,就有i-sum(a[i])个比a[i]大的,复合了 j < i && a[j] > a[i];有的同学可能担心,万一后面又有a[i+k] < a[i]了呢, 那也是一组逆序对 i + k > i && a[i+k] < a[i];注意要先更新。。。在查询。。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 10005;
int n, m, t, k, c[maxn];
struct node
{
int r, l;
}a[maxn*maxn];
void update(int i)
{
while(i <= maxn)
{
c[i] += 1;
i += i & -i;
}
}
int sum(int i)
{
int s = 0;
while(i > 0)
{
s += c[i];
i -= i & -i;
}
return s;
}
int cmp(node a, node b)
{
if(a.l == b.l)
return a.r < b.r;
else
return a.l < b.l;
}
int main()
{
scanf("%d", &t);
int l = 0;
while(t--)
{
memset(c, 0, sizeof(c));
scanf("%d%d%d", &m, &n, &k);
for(int i = 1; i <= k; i++)
{
scanf("%d%d", &a[i].l, &a[i].r);
}
sort(a+1, a+1+k, cmp);
long long ans = 0;
for(int i = 1; i <= k; i++)
{
update(a[i].r); //先更新
ans += i - sum(a[i].r);
}
printf("Test case %d: %lld\n", ++l, ans);
}
return 0;
}