【每日一题】转换为求逆序对 树状数组

链接: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&lt;iostream&gt;
#include&lt;algorithm&gt;

using namespace std;
struct node {
    int x, y, z;
    bool operator &lt; (const node &amp;s) {
        return x &lt; s.x;
    }
}a[200005], b[200005];
long long p;
long long t[200005];
int lowbit(int x) {
    return x &amp; (-x);
}

inline void add(int x, int y) {
    while(x &lt; 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 &gt;&gt; 1;
    cdq(l, mid); cdq(mid + 1, r);
    int L = l, R = mid + 1, tot = l;
    while (L &lt;= mid &amp;&amp; R &lt;= r) {
        if (a[L].y &lt;= a[R].y) add(a[L].z, 1), b[tot++] = a[L++];
        else p -= sum(a[R].z), b[tot++] = a[R++];
    }
    while (L &lt;= mid) {
        add(a[L].z, 1);
        b[tot++] = a[L++];
    }
    while (R &lt;= r) {
      p -= sum(a[R].z);
        b[tot++] = a[R++];
    }
    for(int i = l; i &lt;= mid; i++) add(a[i].z, -1);
    for(int i = l; i &lt;= r; i++) a[i] = b[i];
}
int main() {
    int n;
    cin &gt;&gt; n;
    for(int i = 1; i &lt;= n; i++) {
        cin &gt;&gt; a[i].x &gt;&gt; a[i].y &gt;&gt; a[i].z;
    }
    p = 1LL * n * (n - 1) / 2;
    sort(a + 1, a + 1 + n);
    cdq(1, n);
    cout &lt;&lt; p &lt;&lt; &quot;\n&quot;;
    return 0;
}
全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务