| Time Limit: 1000MS | Memory Limit: 65536K | |
| Total Submissions: 25612 | Accepted: 6930 |
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
Source
题目大意:
问题可以抽象看成上边有一排点,下边有一排点(上边n个)(下边m个),一共要建K条边,问有多少个交叉点(注意,这里在节点处的交叉点不算)、
思路:
1、首先我们明确两条线段会有交叉点的情况:
①对于两条边(a,b),(c,d)需要有a>c&&b<d||a<c&&b>d,才会有交叉点出现。
②我门还能明确这样一个问题:线段的建设时间是不会影响最终结果的。
2、那么我们可以根据上述的第二条,我们对二元组进行排序,使得(x,y)中,x按照从大到小排序,y也从大到小排序,例如样例得到结果:
3 2
3 1
2 3
1 4
那么这个时候我们再按照这个顺序考虑这个问题,其实就不用管第一个数了,我们保证了递减的性质,我们只要找当前y元素之间有多少个比他小的就行了、问题转化到:
2 1 3 4 找逆序对个数。
3、直接暴力找会超时,我们使用树状数组来优化问题,然后剩下的就是实现代码的过程了。
4、注意在节点处的交叉点不算数,所以我们在查询的时候要查询(a【i】.y-1),来去除节点处的交叉点。另外数据会比较大,需要使用long long
Ac代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define ll long long int
struct node
{
ll x,y;
}a[1000015];
ll n,m,q;
ll tree[1000015];//树
ll cmp(node a,node b)
{
if(a.x==b.x)return a.y>b.y;
else return a.x>b.x;
}
ll lowbit(ll x)//lowbit
{
return x&(-x);
}
ll sum(ll x)//求和求的是比当前数小的数字之和,至于这里如何实现,很简单:ll sum=sum(a[i]);
{
ll sum=0;
while(x>0)
{
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
void add(ll x,ll c)//加数据。
{
while(x<=n)
{
tree[x]+=c;
x+=lowbit(x);
}
}
int main()
{
ll t;
ll kase=0;
scanf("%I64d",&t);
while(t--)
{
memset(a,0,sizeof(a));
memset(tree,0,sizeof(tree));
scanf("%lld%lld%lld",&m,&n,&q);
for(ll i=0;i<q;i++)
{
scanf("%lld%lld",&a[i].x,&a[i].y);
}
sort(a,a+q,cmp);
// ll i=0;
ll output=0;
for(int i=0;i<q;i++)
{
output+=sum(a[i].y-1);
add(a[i].y,1);
}
printf("Test case %lld: %lld\n",++kase,output);
}
}
/*
100
5 5 4
3 1
3 2
3 3
1 3
5 5 5
3 1
3 2
3 3
1 3
2 2
5 5 6
2 1
3 1
4 1
5 1
1 2
1 3
4 4 4
1 2
1 2
1 2
2 1
4 4 5
3 1
3 2
2 3
2 4
1 4
3 4 4
1 4
2 3
3 2
3 1
*/
247

被折叠的 条评论
为什么被折叠?



