前置技能:归并排序,树状数组。

cdq分治主要是用来离线解决一些奇怪的问题的。可以用来代替一些高级数据结构比如树套树或者KD-Tree之类的。。。

话说挑战2上的KD-Tree我到现在还没开始学。。。

cdq遇到在线的好像就死掉了?(雾

目前在博主的能力范围内:

主要用来解决多维(三维)偏序问题。

bzoj 陌上花开:给n朵花,每朵花有abc三个属性,问对于每朵花 i 满足 ai>=aj&&bi>=bj&&ci>=cj的花儿有多少。

我们使用cdq减掉一维同时复杂度乘以log。

回想归排求逆序对,其实也就是二维偏序问题,我们在对x排好序的前提下,x可以当成下标,求 xi<xj&&yi>yj的数的数目。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,a[500005],b[500005];ll ans;
void cdq(int l,int r){if(l==r) return;int m=l+r>>1;cdq(l,m);cdq(m+1,r);int i=l,j=m+1,st=l;while (i<=m&&j<=r){if(a[i]<=a[j]){b[st++]=a[i++];} else{ans+=(m-i+1);b[st++]=a[j++];}}while (i<=m) b[st++]=a[i++];while (j<=r) b[st++]=a[j++];for(int i=l;i<=r;i++){a[i]=b[i];}
}
int main(){ios::sync_with_stdio(false);cin>>n;for(int i=1;i<=n;i++)cin>>a[i];cdq(1,n);cout<<ans<<endl;
}

View Code

然后维护左区间对右区间每个数的影响,也就是 ans+=(m-i+1);这句话。

那么我们知道bit也可以解决二维偏序问题,所以我们使用 归并+bit就可以解决三维偏序问题。

关键代码处加了注释

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
struct Flower{int a,b,c,cnt,ans;
}a[N],A[N];
bool cmp2(const Flower &a, const Flower&b){return a.a<b.a||(a.a==b.a&&a.b<b.b)||(a.a==b.a&&a.b==b.b&&a.c<b.c);
}
bool cmp1(const Flower& a,const Flower& b){return a.b<b.b||(a.b==b.b&&a.c<b.c);
}
int n,k,c[N];
int lowbit(int k){ return k&-k;}
void add(int pos,int num){while (pos<=k){c[pos]+=num;pos+=lowbit(pos);}
}
int sum(int x){int ans = 0;while (x){ ans+=c[x];x-=lowbit(x); }return ans;
}
Flower t[N];
void cdq(int l,int r){//l-r满足a非严格递增if(l==r) return;int m = l+r>>1;cdq(l,m);cdq(m+1,r);int j=l;for(int i=m+1;i<=r;i++){for(;j<=m&&A[j].b<=A[i].b;j++)add(A[j].c,A[j].cnt);A[i].ans+=sum(A[i].c);}for(int i=l;i<j;i++)add(A[i].c,-A[i].cnt);int l1=l,l2=m+1; int pos=l;while(l1<=m||l2<=r) {if(l2>r||(l1<=m&&cmp1(A[l1],A[l2]))) t[pos++]=A[l1++];else t[pos++]=A[l2++];}for(int i=l;i<=r;i++) A[i]=t[i];
}int ans[N];
int main(){ios::sync_with_stdio(false);cin>>n>>k;for(int i=1;i<=n;i++){cin>>a[i].a>>a[i].b>>a[i].c;a[i].cnt=1;}sort(a+1,a+1+n,cmp2);//先保证a的大小关系int cnt = 1;for(int i=1;i<=n;i++){if(i==1||!(a[i].a==a[i-1].a&&a[i].b==a[i-1].b&&a[i].c==a[i - 1].c))//处理abc三个属性全部一样的花A[cnt++] = a[i];elseA[cnt-1].cnt++;}cdq(1,cnt-1);for(int i = 1; i <= cnt; i++)ans[A[i].ans+A[i].cnt-1] += A[i].cnt;for(int i = 0; i < n; i++)cout<<ans[i]<<endl;return 0;
}
/**
10 3
3 3 3
2 3 3
2 3 1
3 1 1
3 1 2
1 3 1
1 1 2
1 2 2
1 3 2
1 2 1*//**
3
1
3
0
1
0
1
0
0
1*/

SHOI2007 园丁的烦恼

做法很多。。。这里讲一下cdq。和上一道题是一样的吧,可以这样想,把 t,x,y当成三个变量,t代表了操作时间,可以理解成 查询/添加,

然后对于 (x1,y1)到(x2,y2)可以差分一下,en...

那么这道题就变成了一个  求 满足  ti>tj,xi>=xj,yi>=yi  这样的一个三维偏序问题

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N = 35e5+5;
 5 struct Node{
 6     int op,x,y,w,id;
 7 }a[N],A[N];
 8 int n,m,up,tot;
 9 int c[N];
10 int lowbit(int x){
11     return x&-x;
12 }
13 void upd(int pos,int x){
14     while (pos<=up) {
15         c[pos]+=x;
16         pos+=lowbit(pos);
17     }
18 }
19 int sum(int x){
20     int res = 0;
21     while (x){
22         res+=c[x];
23         x-=lowbit(x);
24     }
25     return res;
26 }
27 void clear(int x){
28     while(x<=up) {
29         if(c[x]) c[x]=0;
30         else break;
31         x+=lowbit(x);
32     }
33 }
34 bool cmp(Node a,Node b){
35     return a.x<b.x||(a.x==b.x&&a.op<b.op);
36 }
37 int ans[N];
38 void cdq(int l,int r){
39     if(l==r) return;
40     int mid = l+r>>1;
41     cdq(l,mid);
42     cdq(mid+1,r);
43     int i=l,j=mid+1,st=l;
44     while (i<=mid&&j<=r){
45         if(cmp(a[i],a[j])){
46             if(a[i].op==0)
47                 upd(a[i].y,1);
48             A[st++]=a[i++];
49         } else{
50             if(a[j].op==1)
51                 ans[a[j].id]+=a[j].w*sum(a[j].y);
52             A[st++]=a[j++];
53         }
54     }
55     while (i<=mid) A[st++]=a[i++];
56     while (j<=r){
57         if(a[j].op==1)
58             ans[a[j].id]+=a[j].w*sum(a[j].y);
59         A[st++] = a[j++];
60     }
61     for(int i=l;i<=r;i++){
62         clear(a[i].y);
63         a[i]=A[i];
64     }
65 }
66 void ins(int op,int x,int y,int w,int id){
67     tot++;
68     a[tot].op=op;a[tot].x=x;a[tot].y=y;a[tot].w=w;a[tot].id=id;
69 }
70 int main(){
71     scanf("%d%d",&n,&m);
72     int x,y;
73     for(int i=1;i<=n;i++){
74         scanf("%d%d",&x,&y);;x++;y++;
75         ins(0,x,y,0,0);
76         up = max(y,up);
77     }
78     int x2,y2;
79     for(int i=1;i<=m;i++){
80         scanf("%d%d%d%d",&x,&y,&x2,&y2);
81         x++;y++;x2++;y2++;
82         ins(1,x2,y2,1,i);
83         ins(1,x-1,y2,-1,i);
84         ins(1,x2,y-1,-1,i);
85         ins(1,x-1,y-1,1,i);
86         up = max(max(y,y2),up);
87     }
88     cdq(1,tot);
89     for(int i=1;i<=m;i++)
90         printf("%d\n",ans[i]);
91     return 0;
92 }

bzoj 3295 动态逆序对

这类问题我们有一个方法就是倒着来一遍吧。然后我们按照时间戳给所有元素标号,每个点 具有  t,x,y 三个属性,就又变成了一个三维偏序问题。

那么我们需要求的 就是 满足(令当前元素为i)  t<ti,x<xi,y>yi的数目加上 t<ti, x>xi,y<yi的数目。

然后我们在归并的时候分别从左往右扫,从右往左扫一下就可以了。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 1e5+5;
 4 struct Node{
 5     int t,x,y;
 6 }a[N],A[N];
 7 int ld[N],yx[N];
 8 int n,m,c[N];
 9 int lowbit(int x){ return x&-x;}
10 void upd(int pos,int x){
11     while (pos<=n){
12         c[pos]+=x;
13         pos+=lowbit(pos);
14     }
15 }
16 void clear(int x){
17     while (x<=n){
18         if(c[x]) c[x]=0,x+=lowbit(x);
19         else break;
20     }
21 }
22 int sum(int x){
23     int res = 0;
24     while (x){
25         res+=c[x];
26         x-=lowbit(x);
27     }
28     return res;
29 }
30 void cdq(int l,int r){//保证x单调增
31     if(l==r) return;
32     int mid = l+r>>1;
33     int s1=l,s2=mid+1;
34     for(int i=l;i<=r;i++){//左边的t全部小于右边的,同时x单调
35         if(a[i].t<=mid)
36             A[s1++]=a[i];
37         else
38             A[s2++]=a[i];
39     }
40     for(int i=l;i<=r;i++)
41         a[i]=A[i];
42     s1=l;s2=mid+1;
43     while (s2<=r){//
44         while (s1<=mid&&a[s1].x<a[s2].x){//x小y大
45             upd(a[s1].y,1);s1++;
46         }
47         ld[a[s2].t]+=(s1-l)-sum(a[s2].y);//左边y比他大的
48         s2++;
49     }
50     for(int i=l;i<=mid;i++)
51         clear(a[i].y);
52     s1=mid;s2=r;
53     while (s2>=mid+1){
54         while (s1>=l&&a[s1].x>a[s2].x){//x大y小
55             upd(a[s1].y,1);
56             --s1;
57         }
58         yx[a[s2].t]+=sum(a[s2].y-1);
59         s2--;
60     }
61     for(int i=l;i<=mid;i++)
62         clear(a[i].y);
63     cdq(l,mid);cdq(mid+1,r);
64 }
65 int pos[N];
66 long long ans[N];
67 int main(){
68     ios::sync_with_stdio(false);
69     cin>>n>>m;
70     for(int i=1;i<=n;i++){
71         cin>>a[i].y;a[i].x=i;pos[a[i].y]=i;
72     }
73     int x,all=n;
74     for(int i=1;i<=m;i++){
75         cin>>x;
76         a[pos[x]].t=all--;
77     }
78     for(int i=1;i<=n;i++){
79         if(!a[i].t)
80             a[i].t=all--;//
81     }
82     cdq(1,n);
83     for(int i=1;i<=n;i++){
84         ans[i]=ans[i-1]+yx[i]+ld[i];
85     }
86     for(int i=n;i>=n-m+1;i--){
87         cout<<ans[i]<<endl;
88     }
89 }

View Code

转载于:https://www.cnblogs.com/MXang/p/10029899.html

赶鸭子上架的cdq分治相关推荐

  1. 【BZOJ-3456】城市规划 CDQ分治 + NTT

    题目链接 http://www.lydsy.com/JudgeOnline/problem.php?id=3456 Solution 这个问题可以考虑dp,利用补集思想 N个点的简单图总数量为$2^{ ...

  2. BZOJ 1176: [Balkan2007]Mokia( CDQ分治 + 树状数组 )

    考虑cdq分治, 对于[l, r)递归[l, m), [m, r); 然后计算[l, m)的操作对[m, r)中询问的影响就可以了. 具体就是差分答案+排序+离散化然后树状数组维护.操作数为M的话时间 ...

  3. bzoj2961 共点圆 (CDQ分治, 凸包)

    /* 可以发现可行的圆心相对于我们要查询的点是在一个半平面上, 然后我们要做的就是动态维护凸壳然后用这个半平面去切它 看看是否是在合法的那一面然后cdq分治就可以了代码基本是抄的,*/#include ...

  4. 洛谷P3122 [USACO15FEB]圈住牛Fencing the Herd(计算几何+CDQ分治)

    题面 传送门 题解 题目转化一下就是所有点都在直线\(Ax+By-C=0\)的同一侧,也就可以看做所有点代入\(Ax+By-C\)之后的值符号相同,我们只要维护每一个点代入直线之后的最大值和最小值,看 ...

  5. 【BZOJ3963】[WF2011]MachineWorks cdq分治+斜率优化

    [BZOJ3963][WF2011]MachineWorks Description 你是任意性复杂机器公司(Arbitrarily Complex Machines, ACM)的经理,公司使用更加先 ...

  6. CDQ分治 + 树状数组 ---- C. Goodbye Souvenir(三维偏序+思维)

    题目链接 题目大意: 给定长度为nnn的数组, 定义数字XXX在[l,r][l,r][l,r]内的值为数字XXX在[l,r][l,r][l,r]内最后一次出现位置的下标减去第一次出现位置的下标 给定m ...

  7. [学习笔记]CDQ分治

    分治,考虑前一半对后一半的影响. (和一般分治不太相同的思想是,一般分治不分谁对谁的影响,跨mid的都要统计.(全局变量统计) 而CDQ貌似要落脚到前一半对后一半的影响上,也就是贡献在后一半统计,由前 ...

  8. BZOJ 1492: [NOI2007]货币兑换Cash [CDQ分治 斜率优化DP]

    传送门 题意:不想写... 扔链接就跑 好吧我回来了 首先发现每次兑换一定是全部兑换,因为你兑换说明有利可图,是为了后面的某一天两种卷的汇率差别明显而兑换 那么一定拿全利啊,一定比多天的组合好 $f[ ...

  9. 【教程】简易CDQ分治教程学习笔记

    前言 辣鸡蒟蒻__stdcall终于会CDQ分治啦!       CDQ分治是我们处理各类问题的重要武器.它的优势在于可以顶替复杂的高级数据结构,而且常数比较小:缺点在于必须离线操作. CDQ分治的基 ...

最新文章

  1. Android支付接入(五):机锋网
  2. 服务器中同一个【ip:port】可以多次accept的问题
  3. Kafka解惑之Old Producer(2)——Sync Analysis
  4. SAP C4C里前台Opportunity搜索的响应明细
  5. 用Cocos2dx开发棋牌游戏的观点解析
  6. 决胜蓝桥杯python组-集合、字典
  7. 安装Oracle 11g 出现交换空间不够
  8. 课时46:魔法方法:描述符(property的原理)
  9. RK3568开发笔记-buildroot移远EC20模块调试记录
  10. 应用安全测试技术DAST、SAST、IAST对比分析-持续更新
  11. MacOS上如何将MOV文件转换为MP4
  12. 评估分形指数和HURST指数预测金融时间序列的能力
  13. IEEE Transactions on Mobile Computing -TMC
  14. 201704 创建财务凭证函数
  15. 手机软件测试规范(含具体用例)
  16. Java基础学习九 多线程
  17. MarkdownPad2 自动生成目录
  18. 西安交通大学大学计算机考试题,西安交通大学17年3月课程考试《计算机应用基础》作业考核试题...
  19. [书籍分享]0-009.微信营销与运营解密:利用微信创造商业价值的奥秘
  20. html输入框的属性,文本框以及input的属性及功能

热门文章

  1. Spring框架中的核心技术之AOP
  2. SQL中ALL、Any、Some的区别
  3. 手机用Postern配置socks5全局代理详细教程
  4. 基于微信小程序的健身器材预订系统的设计与实现
  5. 猜数游戏代码--图形界面
  6. WPF C# VS2013 TextBox控件 限制只能输入数字
  7. web适配手机端开发总结
  8. 今时不同往日:VS2010十大绝技让VS6叹服
  9. skynet日志管理
  10. 3分钟告诉你,智能弱电为啥要学云计算!