• CDQ分治


    前言

    这是一篇普通博客

    这是近期学的最简单的东西了

    终于学懂了,不容易啊QAQ

    这个算法是( ext{CDQ})巨佬发明的,所以得名CDQ分治

    讲解

    这次貌似没有喜闻乐见的百度百科自学了

    CDQ分治用来解决点对的问题

    例如:二维偏序,三维偏序

    这里我们讲三维偏序(即板题)

    板题(洛谷)

    这个东西是不是有点像逆序对((a_j<=a_i,b_j>b_i))?只是又多了一层限制

    根据逆序对的思想,我们先将其按照(a)从小到大排序

    根据逆序对的思想,我们接下来考虑分治

    对于区间([l,mid],[mid+1,r])

    我们分别将其按照(b)排序

    此时这两个区间的(b)是有序的,右区间的任意一个(a)大于左区间的任意一个(a)

    但是两个区间中各自(a)并不是有序的

    现在我们已经做到如果只看这两个区间,(a_{ ext{左区间}}le a_{ ext{右区间}},b_{ ext{左区间}}le b_{ ext{右区间}})

    也就是说,(a,b)都已经满足条件了

    此时只考虑(c),只需要借助树状数组或线段树,用类似于求逆序对的思想即可求解

    但是注意每次求解的时候要清空树状数组

    记得去重 (我本来以为不用的,但是细品......)

    练习

    板题:陌上花开(洛谷)

    代码

    板题代码

    //12252024832524
    #include <cstdio>
    #include <algorithm>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 100005;
    const int MAXK = 200005;
    int n,K,nn;
    int t[MAXK],ans[MAXN];
    struct node
    {
    	int a,b,c,cnt,S;
    }s[MAXN],dz[MAXN];
    
    int Read()
    {
    	int x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    void Put1(int x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    void Put(int x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x);
    	if(c >= 0) putchar(c);
    }
    template <typename T>T Max(T x,T y){return x > y ? x : y;}
    template <typename T>T Min(T x,T y){return x < y ? x : y;}
    template <typename T>T Abs(T x){return x < 0 ? -x : x;}
    
    bool cmp1(node x,node 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;
    }
    int lowbit(int x)
    {
    	return x & (-x);
    }
    void Add(int x,int val)
    {
    	for(int i = x;i <= K;i += lowbit(i)) t[i] += val;
    }
    int qsum(int x)
    {
    	int ret = 0;
    	for(int i = x;i >= 1;i -= lowbit(i)) ret += t[i];
    	return ret;
    }
    void cdq(int l,int r)
    {
    	if(l == r) return;
    	int mid = (l+r) >> 1;
    	cdq(l,mid);
    	cdq(mid+1,r);
    	for(int i = l;i <= r;++ i) dz[i] = s[i];
    	int j = l,i = mid+1,now = l;
    	while(j <= mid && i <= r)
    	{
    		if(dz[j].b <= dz[i].b) Add(dz[j].c,dz[j].S),s[now++] = dz[j++];
    		else dz[i].cnt += qsum(dz[i].c),s[now++] = dz[i++];
    	}
    	while(i <= r) dz[i].cnt += qsum(dz[i].c),s[now++] = dz[i++];
    	for(int fk = l;fk < j;++ fk) Add(dz[fk].c,-dz[fk].S);
    	while(j <= mid) s[now++] = dz[j++];
    }
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	n = Read();
    	K = Read();
    	for(int i = 1;i <= n;++ i)
    	{
    		s[i].a = Read();
    		s[i].b = Read();
    		s[i].c = Read();
    		s[i].S = 1;
    	}
    	sort(s+1,s+n+1,cmp1);
    	nn = 1;
    	for(int i = 2;i <= n;++ i)
    		if(s[i].a == s[i-1].a && s[i].b == s[i-1].b && s[i].c == s[i-1].c) s[nn].S ++;
    		else s[++nn] = s[i];
    //	for(int i = 1;i <= nn;++ i) printf("**%d %d %d %d
    ",s[i].a,s[i].b,s[i].c,s[i].S);
    //	printf("nn : %d
    ",nn);
    	cdq(1,nn);
    	for(int i = 1;i <= nn;++ i) ans[s[i].cnt+s[i].S-1] += s[i].S;
    	for(int i = 0;i < n;++ i) Put(ans[i],'
    ');
    	return 0;
    }
    

    小优化

    其实不用特别把(b)排序,因为我们在做cdq的时候,类似于归并排序(其实就是归并= =),我们直接就可以把(b)排了,往上的时候就不用了特别排序了

    (948ms → 340ms),这就是优化的力量

  • 相关阅读:
    JQuery图片局部放大
    c# .net 如何使用log4net记录日志
    VS2010添加自定义的项目模板及项模板
    Virtualbox运行报cannot access the kernel driver的解决方法
    Session超时设置
    WebBrowser 错误处理
    asp.net MVC 2 自定义用户角色权限设计
    c# 调用CMD不显窗口
    C#遍历CookieContainer所有Cookie并保存到文件
    Application,Session,Cookie,ViewState和Cache区别
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/13392201.html
Copyright © 2020-2023  润新知