赶鸭子上架的cdq分治

Posted mxang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了赶鸭子上架的cdq分治相关的知识,希望对你有一定的参考价值。

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

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];
        else
            A[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
",ans[i]);
91     return 0;
92 }

 

以上是关于赶鸭子上架的cdq分治的主要内容,如果未能解决你的问题,请参考以下文章

算法CDQ分治初探

模板:CDQ分治

初窥CDQ分治

HDU 3507 Print Article(CDQ分治+分治DP)

bzoj4237: 稻草人 cdq分治 单调栈

bzoj 2683 简单题 cdq分治