0)
题目大意:
小岛上东、西各有一列从北到南依次排列的城市,现在将东边与西边的指定城市之间建造高速公路(不同列之间的城市相连),要求给出交叉点个数(假设没有大于两条以上的道路会交于一点)。
转化:
随着连接的线段越来越多,交点个数应该只增不降,而输入数据给出的顺序已经是一个提示,我们假设第一列城市属于l列,第二列城市属于r列,那么l列输入的顺序是从小到大时,可以保证l列当前城市(假设是城市2)连接到的r列某城市(假设是城市3),一定是在l列当前城市之前的城市(假设是城市1)连接到的r列城市之前(假设是4)才会构成交点。因此,我们只需不断返回连接当前线段的同时又加了几个交点即可。而在l列城市顺序输入(编号从小到大)时,而所加交点个数,正对应连接当前线段时被连接到的r列城市(城市3)到最后一个城市(城市6)之间所有城市的(不包括城市3,所以恰好sum(6)-sum(3),而不用sum(6)-sum(3-1))被连接次数之和。于是问题,似乎变成了求任意区间求和,线段树和树状数组都可以解决。
注意:如果题目中给出的城市编号有0,就像下面这张图,那么我们应当将所有城市编号++,再用树状数组或线段树。
1)
#include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const int maxn=1010; struct Road{ int l; int r; }road[maxn*maxn];//注意,道路是城市个数的乘积,不然Runtime int c[maxn]; bool cmp(Road a,Road b){ if(a.l<b.l){ return 1; } else if(a.l==b.l){ return a.r<=b.r;//cout<<a>=b>>endl;如果相等输出什么? } return 0; } int Sum(int x){ long long int sum=0; while(x>0){ sum+=c[x]; x-=(x&(-x)); } return sum; } void Add(int x){ while(x<maxn){ c[x]++; x+=(x&(-x)); } } int main() { int t; scanf("%d",&t); int kase=0; while(t--){ kase++; memset(c,0,sizeof(c)); int n,m,k; scanf("%d%d%d",&n,&m,&k); for(int i=0;i<k;i++){ scanf("%d%d",&road[i].l,&road[i].r); } sort(road,road+k,cmp); long long int res=0; for(int i=0;i<k;i++){ res+=Sum(m)-Sum(road[i].r); Add(road[i].r); } printf("Test case %d: %lld\n",kase,res); } }
Description
Input
Output
Sample Input
1 3 4 4 1 4 2 3 3 2 3 1
Sample Output
Test case 1: 5