• 【算法】CDQ分治 -- 三维偏序 & 动态逆序对


    初次接触CDQ分治,感觉真的挺厉害的。整体思路即分而治之,再用之前处理出来的答案统计之后的答案。

    大概流程是(对于区间 l ~ r):

    1.处理 l ~mid, mid + 1 ~ r 的答案;

    2.分别排序规整;

    3.计算 l ~ mid 中每一个数对 mid + 1 ~ r 中的答案的贡献, 累加;

    4.得到区间l ~ r的答案。

    CDQ分治我一共也才做了两道题目, 就一起整理在这里了。大体都差不多,CDQ+树状数组分别维护两个维度。

    1.三维偏序

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 3000000
    #define lowbit(x) x &(-x)
    int n, k, tot, ans[maxn], c[maxn];
    struct node
    {
        int a, b, c, ans, cnt;
    }num[maxn], a[maxn];
    
    int read()
    {
        int x = 0, k = 1;
        char c;
        c = getchar();
        while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar();}
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * k;
    }
    
    bool cmp(node a, node b)
    {
        if(a.a != b.a) return a.a < b.a;
        if(a.b != b.b) return a.b < b.b;
        return a.c < b.c;
    }
    
    bool cmp2(node a, node b)
    {
        if(a.b != b.b) return a.b < b.b;
        return a.c < b.c; 
    }
    
    void Update(int x, int v)
    {
        for(int i = x; i <= k; i += lowbit(i))
            c[i] += v;
    }
    
    int Query(int x)
    {
        int ans = 0;
        for(int i = x; i; i -= lowbit(i))
            ans += c[i];
        return ans;
    }
    
    void cdq(int l, int r)
    {
        int mid = (l + r) >> 1;
        if(r - l >= 2) cdq(l, mid), cdq(mid + 1, r);
        if(r == l) return;
        sort(num + l, num + mid + 1, cmp2);
        sort(num + mid + 1, num + r + 1, cmp2);
        int i = l, j = mid + 1;
        while(i <= mid && j <= r)
        {
            if(num[i].b <= num[j].b) Update(num[i].c, num[i].cnt), i ++;
            else num[j].ans += Query(num[j].c), j ++;
        }
        while(i <= mid) Update(num[i].c, num[i].cnt), i ++;
        while(j <= r) num[j].ans += Query(num[j].c), j ++;
        for(int i = l; i <= mid; i ++) Update(num[i].c, -num[i].cnt);
    }
    
    int main()
    {
        n = read(), k = read();
        for(int i = 1; i <= n; i ++)
            a[i].a = read(), a[i].b = read(), a[i].c = read();
        sort(a + 1, a + 1 + n, cmp);
        for(int i = 1; i <= n;)
        {
            int j = 1;
            while(i + j <= n && a[i].a == a[i + j].a && a[i].b == a[i + j].b && a[i].c == a[i + j].c) j ++;
            num[++ tot] = a[i];
            num[tot].cnt = j;
            i += j;
        }
        cdq(1, tot);
        for(int i = 1; i <= tot; i ++) ans[num[i].ans + num[i].cnt - 1] += num[i].cnt;
        for(int i = 0; i < n; i ++) printf("%d
    ", ans[i]);
        return 0;
    }

    2.动态逆序对

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 2000000
    #define ll long long
    #define lowbit(x) x & (-x)
    int n, m, timer, a[maxn], b[maxn], d[maxn], t[maxn];
    ll ans[maxn], c[maxn];
    struct node
    {
        int t, num, pl;
        ll ans;
    }w[maxn];
    
    int read()
    {
        int x = 0, k = 1;
        char c;
        c = getchar();
        while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar();}
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * k;
    }
    
    bool cmp(node a, node b)
    {
        if(a.t != b.t) return a.t < b.t;
        else return a.pl < b.pl;
    }
    
    bool cmp2(node a, node b)
    {
        return a.pl < b.pl;
    }
    
    bool cmp3(node a, node b)
    {
        return a.pl > b.pl;
    }
    
    void add(int x, int num)
    {
        for(int i = x; i <= n; i += lowbit(i))
            c[i] += num;
    }
    
    ll query(int x)
    {
        ll ans = 0;
        for(int i = x; i; i -= lowbit(i))
            ans += c[i];
        return ans;
    }
      
    void CDQ(int l, int r)//位置在我之前,num>我的 
    {
        int mid = (l + r) >> 1;
        if(r - l >= 2) CDQ(l, mid), CDQ(mid + 1, r);
        if(l == r) return;
        sort(w + l, w + 1 + mid, cmp2);
        sort(w + mid + 1, w + r + 1, cmp2);
        int i = l, j = mid + 1;
        while(i <= mid && j <= r)
        {
            if(w[i].pl < w[j].pl) add(w[i].num, 1), i ++;
            else w[j].ans += (query(n) - query(w[j].num)), j ++; 
        }
        while(i <= mid) add(w[i].num, 1), i ++;
        while(j <= r) w[j].ans += (query(n) - query(w[j].num)), j ++;
        for(int i = l; i <= mid; i ++)
            add(w[i].num, -1);
    }
    
    void CDQ2(int l, int r)//位置在我之后,num<我的 
    {
        int mid = (l + r) >> 1;
        if(r - l >= 2) CDQ2(l, mid), CDQ2(mid + 1, r);
        if(l == r) return;
        sort(w + l, w + 1 + mid, cmp3);
        sort(w + mid + 1, w + r + 1, cmp3);
        int i = l, j = mid + 1;
        while(i <= mid && j <= r)
        {
            if(w[i].pl > w[j].pl) add(w[i].num, 1), i ++;
            else w[j].ans += (query(w[j].num)), j ++; 
        }
        while(i <= mid) add(w[i].num, 1), i ++;
        while(j <= r) w[j].ans += (query(w[j].num)), j ++;
        for(int i = l; i <= mid; i ++)
            add(w[i].num, -1);
    }
    
    
    int main()
    {
        n = read(), m = read();
        for(int i = 1; i <= n; i ++)
        {
            a[i] = read();
            b[a[i]] = i;
        }
        timer = m;
        for(int i = 1; i <= m; i ++)
        {
            d[i] = read();
            t[b[d[i]]] = timer --;
        }
        for(int i = 1; i <= n; i ++) w[i].t = t[i], w[i].num = a[i], w[i].pl = i;
        sort(w + 1, w + 1 + n, cmp);
        CDQ(1, n);
        for(int i = 1; i <= n; i ++) ans[w[i].t] += w[i].ans;
        for(int i = 1; i <= n; i ++) w[i].t = t[i], w[i].num = a[i], w[i].pl = i, w[i].ans = 0;
        sort(w + 1, w + 1 + n, cmp);
        memset(c, 0, sizeof(c));
        CDQ2(1, n);
        for(int i = 1; i <= n; i ++) ans[w[i].t] += w[i].ans;
        for(int i = 1; i <= m; i ++) ans[i] += ans[i - 1];
        for(int i = m; i >= 1; i --) printf("%lld
    ", ans[i]);
        return 0;
    }
  • 相关阅读:
    网络爬虫之框架(Scrapy)
    模拟投币试验
    [LeetCode#177]Nth Highest Salary
    Windows Server 2008 各版本功能差异与比较各版本概观--转载
    Win2008 R2下Server Core常用命令小结
    powershell 中用Sqlps管理我台sqlserver 2008r2
    初识 Markdown
    React 入门(3): 严格模式 ReactDOM
    ES6 类的正确定义方式 公有类字段 getter / setter
    Lodash 去抖动 节流
  • 原文地址:https://www.cnblogs.com/twilight-sx/p/8451991.html
Copyright © 2020-2023  润新知