cdq分治/逆序对 一点点总结

cdq分治/逆序对 一点点总结

归并排序求普通逆序对问题

复制代码
#include<bits/stdc++.h>
#define IN inline
#define R register int
using namespace std;
const int N=5e5+5;    
typedef long long ll;
IN int read()
{
    int f=1;char ch;
    while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;int x=ch-'0';
    while((ch=getchar())>='0'&&ch<='9') x=x*10+ch-'0';
    return x*f;
}
#define in(x) x=read()
ll ans;
int n,a[N],b[N],k;
void merge_sort(int l,int r)
{
    if(l==r) return ;
    int mid=l+r>>1;
    merge_sort(l,mid);merge_sort(mid+1,r);
    int i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r)
    {
        if(a[i]<=a[j]) b[k++]=a[i++];
        else 
        {
            ans+=(ll)mid-i+1;
            b[k++]=a[j++];
        }
    } 
    while(i<=mid) b[k++]=a[i++];
    while(j<=r) b[k++]=a[j++];
    for(R i=l;i<k;i++) a[i]=b[i];
}
int main()
{
    in(n);
    for(R i=1;i<=n;i++) a[i]=read();
    merge_sort(1,n);
    printf("%lld",ans);
    return 0;
} 
复制代码

1.注意不能重复统计

因为一对逆序对(ai,aj)只要在i位置或者j位置统计就好了,所以只要在if else的一个分支里面统计就行

2.统计答案的时机

只要ai > aj,那么mid + 1 ~ j 都比ai小,这时候直接统计就行

 

cdq分治求三维偏序

复制代码
#include <bits/stdc++.h>
#define R(x) x=read()
using namespace std;

inline int read()
{
    int x = 0, f = 1;char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

const int N = 1e5 + 5, M = 2e5 + 5;

struct Element{
    int a, b, c, cnt, res;
    bool operator != (const Element &tmp) const
    {
        if(a != tmp.a) return true;
        if(b != tmp.b) return true;
        if(c != tmp.c) return true;
        return false;
    }
};

bool cmpA(Element x, Element y)
{
    if(x.a != y.a)  return x.a < y.a;
    if(x.b != y.b)  return x.b < y.b;
    return x.c < y.c;
}

Element e[N], re[N];

int n, k, idx;

//树状数组

int lowbit(int x)
{
    return x & -x;
}

int tr[M];

void update(int x, int v)
{
    for(int i = x; i <= k; i+=lowbit(i))
        tr[i] += v;
    return;
}

int query(int x)
{
    int res = 0;
    for(int i = x; i; i-=lowbit(i))
        res += tr[i];
    return res;
}

//临时数组
Element tmpE[N];

bool cmpB(Element x, Element y)
{
    if(x.b != y.b)  return x.b < y.b;
    return x.c < y.c;
}

void solve(int l, int r)
{
    if(l == r)
        return;
    int mid = l + r >> 1;
    solve(l, mid); solve(mid + 1, r);
    int i = l, j = mid + 1, k = l;
    /*while(i <= mid && j <= r)
    {
        if(re[i].b <= re[j].b) 
        {
            update(re[i].c, re[i].cnt);
            re[j].res += query(re[j].c);
            tmpE[k++] = re[i++];
        }
        else
        {
            tmpE[k++] = re[j++];
        }
    }   
    int bI = i;
    while(i <= mid) 
    {
        tmpE[k++] = re[i++];
    }
    while(j <= r)
    {
        re[j].res += query(re[j].c);
        tmpE[k++] = re[j++];
    }
    for(i = l; i <= r; i++)
    {
        if(i < bI)
            update(re[i].c, -re[i].cnt);
        re[i] = tmpE[i];
    }*/
    while(j <= r)
    {
        while(i <= mid && re[i].b <= re[j].b)
        {
            update(re[i].c, re[i].cnt);
            tmpE[k++] = re[i];
            i++;
        }
        re[j].res += query(re[j].c);
        tmpE[k++] = re[j++];
    }
    for(int iI = l; iI < i; iI++)
        update(re[iI].c, -re[iI].cnt);
    while(i <= mid)
        tmpE[k++] = re[i++];
    for(i = l; i <= r; i++)
        re[i] = tmpE[i];
    //sort(re + l, re + r + 1, cmpB);
}

int ans[N];

int main()
{
    R(n);R(k);
    for(int i = 1; i <= n; i++)
    {
        R(e[i].a);R(e[i].b);R(e[i].c);
    }
    //去重
    sort(e + 1, e + n + 1, cmpA);
    int tmpCnt = 0;
    for(int i = 1; i <= n; i++)
    {
        tmpCnt++;
        if(e[i] != e[i + 1])
        {
            re[++idx] = e[i];
            re[idx].cnt = tmpCnt;
            re[idx].res = 0;
            tmpCnt = 0;
        }
    }
    solve(1, idx);
    for(int i = 1; i <= idx; i++)
        ans[re[i].cnt - 1 + re[i].res] += re[i].cnt;
    for(int i = 0; i < n; i++)
        printf("%d\n", ans[i]);
    return 0;
}
复制代码

统计时机

这个统计时机比普通的逆序对更特殊,不能一出现ai>aj就直接统计,因为这个时候所有能够和aj构成逆序对的数有可能还没有进来,如果每次一遇到ai>aj就统计,就会导致答案重复累加,最终偏大。

所以,我们要把朴素逆序对里的if改成while循环,把所有满足条件的i都遍历过之后,再统计j的贡献。

 

本文作者:Gold_stein

本文链接:https://www.cnblogs.com/smartljy/p/18157936

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Gold_stein  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
🔑
  1. 1 逃离地面 RAD & 三浦透子
逃离地面 - RAD & 三浦透子
00:00 / 00:00
An audio error has occurred.

作词 : 野田洋次郎

作曲 : 野田洋次郎

空飛ぶ羽根と引き換えに 繋ぎ合う手を選んだ僕ら

それでも空に魅せられて 夢を重ねるのは罪か

夏は秋の背中を見て その顔を思い浮かべる

憧れなのか、恋なのか 叶わぬと知っていながら

重力が眠りにつく 1000年に一度の今日

太陽の死角に立ち 僕らこの星を出よう

彼が眼を覚ました時 連れ戻せない場所へ

「せーの」で大地を蹴って ここではない星へ

行こう

もう少しで運命の向こう もう少しで文明の向こう

もう少しで運命の向こう もう少しで

夢に僕らで帆を張って 来たるべき日のために夜を超え

いざ期待だけ満タンで あとはどうにかなるさと 肩を組んだ

怖くないわけない でも止まんない

ピンチの先回りしたって 僕らじゃしょうがない

僕らの恋が言う 声が言う

「行け」と言う