【每日一题】转换为求逆序对 树状数组
链接:https://ac.nowcoder.com/acm/problem/13947
n支队伍一共参加了三场比赛。
一支队伍x认为自己比另一支队伍y强当且仅当x在至少一场比赛中比y的排名高。
求有多少组(x,y),使得x自己觉得比y强,y自己也觉得比x强。
(x, y), (y, x)算一组。
4
1 3 1
2 2 4
4 1 2
3 4 3
二元组满足条件当且仅当一场比赛x的排名高而另一场y的排名高。
方法1:
只考虑两场不就是求逆序对吗?
三场的话就
按比赛1排序:
即 1 2 4 3 (序号排名 真实值是 1 2 3 4)
3 2 4 1
1 4 3 2 ---逆序对 4,3 4,2(第一场是顺序的,即前面的排名高)
再按比赛2排序求比赛3的逆序
两个相加就是答案
#include <bits/stdc++.h> using namespace std; #define ll long long #define MYINTMAX 0x3f3f3f3f #define N 200005 int tree[N]; int gn=0; int gi=0; inline int query(int pos){ int ans=0; while(pos>0) { ans+=tree[pos]; pos-=pos&(-pos); } return ans; } inline void update(int pos, int val){ int ans = 0; while(pos<=gn){ tree[pos]+=val; pos+=pos&(-pos); } } int cmp(const void *p1,const void*p2){ if( ((int*)p1)[gi]!=((int*)p2)[gi]) return ((int*)p1)[gi] - ((int*)p2)[gi]; } int a[N][3]; int b[3][N]; ll solveForRever(int *a){ ll res=0; memset(tree,0,sizeof(int)*(gn+1)); for(int i=gn-1;i>=0;i--){ res+=query(a[i]); update(a[i],1); } return res; } void copyNum(int level) { for(int i=0;i<gn;++i){ //cout<<a[i][level]; b[level][i] = a[i][level]; } return; } int main(void) { int n; cin>>n; gn=n; for(int i = 0;i<n;++i){ for(int j=0;j<3;++j){ cin>>a[i][j]; } } qsort(a,n,sizeof(int)*3,cmp); /*for(int i = 0;i<n;++i){ for(int j=0;j<3;++j){ cout<<a[i][j]; } cout<<endl; }*/ copyNum(1); copyNum(2); ll ans; ans = solveForRever(b[1]); ans+=solveForRever(b[2]); gi=1; qsort(a,n,sizeof(int)*3,cmp); copyNum(2);copyNum(0); ans+=solveForRever(b[2]);ans+=solveForRever(b[0]); gi=2; qsort(a,n,sizeof(int)*3,cmp); copyNum(1);copyNum(0); ans+=solveForRever(b[1]);ans+=solveForRever(b[0]); cout<<ans/4; return 0; }
方法二:cbq分治
不符合的情况是一个三维偏序
主要思想就是先按照第一维排序。然后遍历每一个点,此时我们要统计的就是前面的点中比这个点的y坐标要小的点的个数。我们用一个树状数组来维护y坐标这个信息,于是就只要得到getsum(y)就行了。
三维的CDQ分治呢,做法如下:
第一维排序,第二维CDQ分治,第三维树状数组。
第一维比如先按照x坐标排序。在第二维的CDQ分治时,我们对每一个自区间,先按照y排序,计算左边对右边的影响的时候:
左边的x都小于右边
在每一边y也是依次递增的
我们只要扫描右边,把左边y小于等于当前的y坐标的z坐标更新到树状数组,统计目前树状数组z坐标小于自己的就是偏序<的点的个数。
#include<iostream> #include<algorithm> using namespace std; struct node { int x, y, z; bool operator < (const node &s) { return x < s.x; } }a[200005], b[200005]; long long p; long long t[200005]; int lowbit(int x) { return x & (-x); } inline void add(int x, int y) { while(x < 200005) { t[x] += y; x += lowbit(x); } } inline long long sum(int x) { long long ans = 0; while(x) { ans += t[x]; x -= lowbit(x); } return ans; } void cdq(int l, int r) { if(l == r) return ; int mid = l + r >> 1; cdq(l, mid); cdq(mid + 1, r); int L = l, R = mid + 1, tot = l; while (L <= mid && R <= r) { if (a[L].y <= a[R].y) add(a[L].z, 1), b[tot++] = a[L++]; else p -= sum(a[R].z), b[tot++] = a[R++]; } while (L <= mid) { add(a[L].z, 1); b[tot++] = a[L++]; } while (R <= r) { p -= sum(a[R].z); b[tot++] = a[R++]; } for(int i = l; i <= mid; i++) add(a[i].z, -1); for(int i = l; i <= r; i++) a[i] = b[i]; } int main() { int n; cin >> n; for(int i = 1; i <= n; i++) { cin >> a[i].x >> a[i].y >> a[i].z; } p = 1LL * n * (n - 1) / 2; sort(a + 1, a + 1 + n); cdq(1, n); cout << p << "\n"; return 0; }