题意:
有n支队伍参加n3场比赛,一支队伍x认为自己比另一支队伍y强当且仅当x在至少一场比赛中比y的排名高,也就是互相赢过对方,这样算一组(x,y),试问存在多少组?
分析:
我们首先想到的思路是将第一场比赛进行排序,保证i自认为比i+1强,然后寻找后面比赛存在多少组j比j+1强,这样暴力下来复杂度要O(n^2),常熟还很大,显然会TLE,那么我们想一想怎么优化这种暴力。
我们先从总情况上考虑,符合队伍的x,y三场比赛后可能两种情况:rankx>ranky2次或者是rankx<ranky2次,那么我们统计这些情况出现的次数cnt,因为满足情况的(x,y)两种情况都会统计一次,存在重复,所以最终答案应为cnt/2。
而统计上述情况,我们可以由2场比赛进行考虑,设第一场比赛用a表示,第二场比赛用b表示,我们首先按a进行正序排序,那么问题就变成求b组j>i且bi>bj出现数量,也就是求b组的逆序对。
对于逆序对问题,方法有归并排序,树状数组和线段树。
线段树写bug修不过来,所以参考了一下大佬的树状数组作法(树状数组真的比线段树好看一万倍)
具体实现:
每次构建一个值的范围是1~n的BIT,按照j = 0,1,2,3···,n-1的顺序进行如下操作。
1、BIT t[j] 位置加上 1
2、res += j - query(t[i])
对于目标数组b,区间[bi+1,n]之和,即为>bi值的个数,最后遍历统计即可,记得除2。
最后提醒一下,如果通过率45%,记得开longlong!
代码:
#include<stdio.h> #include<algorithm> #include<string.h> #include<math.h> using namespace std; typedef long long ll; int i,j,n,k,tt,m; ll cnt,ans; int ran[3][200003],t[200003]; struct Hi{ int x,y; }a[200003]; int cmp(Hi A,Hi B){ if(A.x==B.x) return A.y<B.y; return A.x<B.x; } void add(int x, int v) { for(int i = x; i <= n; i += i & -i) t[i] += v; } int get(int x) { int res = 0; for(int i = x; i; i -= i & -i) res += t[i]; return res; } using namespace std; int main() { scanf("%d",&n); for(i=1;i<=n;i++){ for(j=0;j<3;j++){ scanf("%d",&ran[j][i]); } } for(int i=0;i<2;i++){ for(int j=i+1;j<3;j++){ for(k=1;k<=n;k++){ a[k].x=ran[i][k]; a[k].y=ran[j][k]; } sort(a+1,a+1+n,cmp);//先排序第一组 memset(t,0,sizeof(t)); for(int k=1;k<=n;k++){//求逆序对数 ans+=get(n)-get(a[k].y);//得到之前有多少个比你大的数(逆序对) add(a[k].y,1);//下标等效与数值,该位置加上1 } } } printf("%lld\n",ans/2);//答案有重复要除2 }