zoukankan      html  css  js  c++  java
  • 赶鸭子上架的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 }

    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
  • 相关阅读:
    《家庭财务总管》升级了(1.0.0.1)
    谁拥有接口?
    VScode调试C++工程
    NVIDIA显卡原生管理查询功能nvidiasmi的部分使用功能
    python版本的两款NVIDIA显卡管理查询工具
    pytorch之网络参数统计 torchstat & torchsummary
    电脑、笔记本、手机维修经验分享网站,专业领域网站
    Python使用pynvml查看GPU信息
    【转载】 Ubuntu下使用VSCode的launch.json及tasks.json编写
    笔记本挑电源适配器吗,是不是电压相同情况下保证功率大于等于原装适配器就可以保证笔记本正常运行???
  • 原文地址:https://www.cnblogs.com/MXang/p/10029899.html
Copyright © 2011-2022 走看看