• cdq分治学习笔记


    1.作用

    可以用来搞一些离线的题目,用于代替复杂的数据结构入树套树。

    2.做法

    1.分治左边区间。

    2.计算左边区间对右边区间答案的贡献。

    3.分治右边区间。

    4.将当前区间排序。

    5.return。

    3.例题

    1.树状数组

    单点修改区间查询。

    将所有操作记录cdq分治。

    每次分治时递归完了如果在左区间并且是添加操作就反手将sum+=jia,如果是询问就把ans[]+=sum。

    莫得代码。

    2.经典三维偏序。

    【模板】三维偏序(陌上花开)

    设三维分别为a,b,c。

    1.第一维排好序后分治,第二位用归并排序保证在这个地方询问时前面的b比当前的b小,然后第三位用树状数组压入,查询c比当前晓得。

    2.只有在a在左区间时才执行添加操作,a在右区间时执行询问操作,这样就保证了询问的a永远大于加入的a。

    3.总结就是第一维靠分治保证小于,第二维靠归并排序,第三位靠树状数组。

    4.递归结束时要清空树状数组。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    int n,k,shu[200005],dui[100005],cnt,daan[100005],ans[100005];
    struct pigu
    {
        int xx,yy,zz,id;
    }a[100005];
    int cmp1(pigu x,pigu y)
    {
        if(x.xx!=y.xx)
            return x.xx<y.xx;
        if(x.yy!=y.yy)
            return x.yy<y.yy;
        if(x.zz!=y.zz)
            return x.zz<y.zz;
    }
    int cmp2(pigu x,pigu y)
    {
        if(x.yy!=y.yy)
            return x.yy<y.yy;
        if(x.zz!=y.zz)
            return x.zz<y.zz;
        return x.xx<y.xx;
    }
    int lowbit(int x)
    {
        return x&(-x);
    }
    void cdq(int l,int r)
    {
        if(l==r)
            return;
        int mid=(l+r)>>1;
        cdq(l,mid);
        cdq(mid+1,r);
        sort(a+l,a+r+1,cmp2);
        for(int i=l;i<=r;i++)
            if(a[i].xx<=mid)
            {
                int now=a[i].zz;
                while(now<=k)
                {
                    shu[now]++;
                    now+=lowbit(now);
                }
            }
            else
            {
                int now=a[i].zz;
                while(now)
                {
                    daan[a[i].id]+=shu[now];
                    now-=lowbit(now);
                }
            }
        for(int i=l;i<=r;i++)
            if(a[i].xx<=mid)
            {
                int now=a[i].zz;
                while(now<=k)
                {
                    shu[now]--;
                    now+=lowbit(now);
                }
            }
    }
    int main()
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d%d",&a[i].xx,&a[i].yy,&a[i].zz);
            a[i].id=i;
        }
        sort(a+1,a+n+1,cmp1);
        for(int i=1;i<=n;)
        {
            int j=i+1;
            while(a[i].xx==a[j].xx&&a[i].yy==a[j].yy&&a[i].zz==a[j].zz)
                j++;
            ++cnt;
            while(i<j)
            {
                dui[a[i].id]=a[j-1].id;
                i++;
            }
        }
        for(int i=1;i<=n;i++)
            a[i].xx=i;
        cdq(1,n);
        for(int i=1;i<=n;i++)
        {
            ans[daan[dui[a[i].id]]]++;
        }
        for(int i=0;i<n;i++)
            printf("%d
    ",ans[i]);
        return 0;
    }
    /*
    3 3
    1 2 3
    0 1 2
    1 2 3
    */
    View Code

    3.P2163 [SHOI2007]园丁的烦恼

    给出了一些点,一堆询问求一个矩形内有几个点。

    一开始感觉是三维偏序,要两个log,结果发现时间没用,就成二维偏序了。

    cdq分治,二维偏序,将询问拆成四个(跟矩阵前缀和差不多)就可以了。

    排序时先考虑y再考虑是询问还是添加。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5000005;
    int n,m,cnt,ma,ans[N];
    struct pigu
    {
        int pan,x,y,zai,xp;
        friend bool operator <(pigu x,pigu y)
        {
            return x.y<=y.y;
        }
    }a[N],hu[N];
    inline bool cmp(pigu x,pigu y)
    {
        return x.x==y.x?(x.y==y.y?x.pan<y.pan:x.y<y.y):x.x<y.x;
    }
    inline int read()
    {
        char c=getchar();
        int x=0,f=1;
        while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
        while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
        return x*f;
    }
    inline void solve(int l,int r)
    {
        if(l==r) return;
        int mid=(l+r)>>1;
        solve(l,mid);solve(mid+1,r);
        int o=l,ll=l,mm=mid+1,daan=0;
        while(ll<=mid&&mm<=r)
        {
            if(a[ll]<a[mm])
            {
                if(a[ll].pan==1) daan++;
                hu[o++]=a[ll++];
            }
            else
            {
                if(a[mm].pan==2) ans[a[mm].zai]+=a[mm].xp*daan;
                hu[o++]=a[mm++];
            }
        }
        while(ll<=mid) hu[o++]=a[ll++];
        while(mm<=r)
        {
            if(a[mm].pan==2) ans[a[mm].zai]+=a[mm].xp*daan;
            hu[o++]=a[mm++];
        }
        for(int i=l;i<=r;i++) a[i]=hu[i];
    }
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=n;i++)
        {
            a[++cnt].x=read()+1;a[cnt].y=read()+1;ma=max(ma,a[cnt].y);
            a[cnt].pan=1;
        }
        for(int i=1,x,y,xx,yy;i<=m;i++)
        {
            x=read()+1;y=read()+1;xx=read()+1;yy=read()+1;
            a[++cnt].pan=2;a[cnt].x=x-1,a[cnt].y=y-1;a[cnt].zai=i;a[cnt].xp=1;
            a[++cnt].pan=2;a[cnt].x=x-1;a[cnt].y=yy;a[cnt].zai=i;a[cnt].xp=-1;
            a[++cnt].pan=2;a[cnt].x=xx;a[cnt].y=y-1;a[cnt].zai=i;a[cnt].xp=-1;
            a[++cnt].pan=2;a[cnt].x=xx;a[cnt].y=yy;a[cnt].zai=i;a[cnt].xp=1;
        }
        sort(a+1,a+cnt+1,cmp);
        solve(1,cnt);
        for(int i=1;i<=m;i++) cout<<ans[i]<<"
    ";
    }    
    View Code

    4.P4390 [BOI2007]Mokia 摩基亚

    就是上一个题增加了一手修改,就再加上时间,就成了三维偏序,每个询问还是拆成四个:查询时间小于此询问,x<X,y<Y的修改。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2000005;
    int n,m,cnt,ma,ans[N],s[N];
    struct pigu
    {
        int pan,x,y,zai,xp,jia;
        friend bool operator <(pigu x,pigu y)
        {
            return x.x==y.x?(x.y==y.y?x.pan<y.pan:x.y<y.y):x.x<y.x;
        }
    }a[N],hu[N];
    inline bool cmp(pigu x,pigu y)
    {
        return x.x==y.x?(x.y==y.y?x.pan<y.pan:x.y<y.y):x.x<y.x;
    }
    inline int lowbit(int x)
    {
        return x&(-x);
    }
    inline void add(int x,int ad)
    {
        for(int i=x;i<=ma;i+=lowbit(i)) s[i]+=ad;
    }
    inline int query(int x)
    {
        int daan=0;
        while(x)
        {
            daan+=s[x];
            x-=lowbit(x);
        }
        return daan;
    }
    inline int read()
    {
        char c=getchar();
        int x=0,f=1;
        while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
        while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
        return x*f;
    }
    inline void solve(int l,int r)
    {
        if(l==r) return;
        int mid=(l+r)>>1;
        solve(l,mid);solve(mid+1,r);
        int o=l,ll=l,mm=mid+1;
        while(ll<=mid&&mm<=r)
        {
            if(a[ll]<a[mm])
            {
                if(a[ll].pan==1) add(a[ll].y,a[ll].jia); 
                hu[o++]=a[ll++];
            }
            else
            {    
                if(a[mm].pan==2) ans[a[mm].zai]+=a[mm].xp*query(a[mm].y);
                hu[o++]=a[mm++];
            }
        }
        while(ll<=mid) add(a[ll].y,a[ll].jia),hu[o++]=a[ll++];
        while(mm<=r)
        {
            if(a[mm].pan==2) ans[a[mm].zai]+=a[mm].xp*query(a[mm].y);
            hu[o++]=a[mm++];
        }
        for(int i=l;i<=mid;i++) if(a[i].pan==1) add(a[i].y,-a[i].jia);
        for(int i=l;i<=r;i++) a[i]=hu[i];
    }
    int main()
    {
        int xipan=read(),gu=0;
        while(xipan!=3)
        {
            int x,y,xx,yy;
            if(xipan==0) ma=read()+1;
            if(xipan==1)
            {
                a[++cnt].x=read()+1;a[cnt].pan=1;a[cnt].y=read()+1;a[cnt].jia=read();
            }
            if(xipan==2)
            {
                x=read()+1;y=read()+1;xx=read()+1;yy=read()+1;
                a[++cnt].pan=2;a[cnt].x=x-1,a[cnt].y=y-1;a[cnt].zai=++gu;a[cnt].xp=1;
                a[++cnt].pan=2;a[cnt].x=x-1;a[cnt].y=yy;a[cnt].zai=gu;a[cnt].xp=-1;
                a[++cnt].pan=2;a[cnt].x=xx;a[cnt].y=y-1;a[cnt].zai=gu;a[cnt].xp=-1;
                a[++cnt].pan=2;a[cnt].x=xx;a[cnt].y=yy;a[cnt].zai=gu;a[cnt].xp=1;
            }
            if(xipan==3) break;
            xipan=read();
        }
        solve(1,cnt);
        for(int i=1;i<=gu;i++) cout<<ans[i]<<"
    ";
    }
    View Code

     

    5.P4027 [NOI2007]货币兑换

    首先斜率优化那个样子搞一哈虱子,然后发现x不递增,斜率不递增,就可以用splay动态维护凸包,二分查找最近的斜率。

    还可以cdq分治一波,先总体全部按照k排序,左区间搞一手斜率单调递减,右区间由于k拍好了序,就可以直接在左边维护好的东西头查询。

    注意每次操作前要先把整个区间按照时间排序拍好,递归完了就可以不需要管时间了。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+5;
    const double eps=1e-7;
    const double inf=1e10;
    int n;
    double f[N];
    struct pigu
    {
        double x,y,a,b,r,k;
        int id;
    }wen[N],hu[N],hu2[N];
    inline int read()
    {
        char c=getchar();
        int x=0,f=1;
        while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
        while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
        return x*f;
    }
    inline bool cmp(pigu x,pigu y)
    {
        return x.k<y.k;
    }
    inline double xl(int x,int y)
    {
        if(fabs(wen[x].x-wen[y].x)<=eps) return inf;
        return (wen[x].y-wen[y].y)/(wen[x].x-wen[y].x);
    }
    int st[N];
    inline void merge(int l,int r)
    {
        int mid=(l+r)>>1;
        int t1=l,t2=mid+1;
        for(int i=l;i<=r;++i)
            if(t1<=mid&&(t2>r||wen[t1].x<wen[t2].x+eps)) hu[i]=wen[t1++];
            else hu[i]=wen[t2++];
        for(int i=l;i<=r;++i) wen[i]=hu[i];
    }
    inline void solve(int l,int r)
    {
        if(l==r)
        {
            f[l]=max(f[l],f[l-1]);
            wen[l].y=f[l]/(wen[l].a*wen[l].r+wen[l].b);
            wen[l].x=wen[l].y*wen[l].r;
            return;
        }
        int mid=(l+r)>>1,ll=l-1,mm=mid;
        for(int i=l;i<=r;i++)
        {
            if(wen[i].id<=mid) hu[++ll]=wen[i];
            else hu[++mm]=wen[i];    
        }
        for(int i=l;i<=r;i++) wen[i]=hu[i];
        solve(l,mid);
        int top=0;
        for(int i=l;i<=mid;i++)
        {
            while(top>=2&&xl(st[top],st[top-1])<xl(i,st[top])+eps) top--;
            st[++top]=i;
        }
        for(int i=mid+1;i<=r;i++)
        {
            while(top>1&&xl(st[top],st[top-1])<wen[i].k+eps) top--;
            f[wen[i].id]=max(f[wen[i].id],wen[i].a*wen[st[top]].x+wen[i].b*wen[st[top]].y);
        }
        solve(mid+1,r);merge(l,r);
    }
    int main()
    {
        n=read();scanf("%lf",&f[0]);
        for(int i=1;i<=n;i++)
        {
            scanf("%lf%lf%lf",&wen[i].a,&wen[i].b,&wen[i].r);
            wen[i].k=-wen[i].a/wen[i].b;wen[i].id=i;
        }
        sort(wen+1,wen+n+1,cmp);
        solve(1,n);
        printf("%.3lf",f[n]);
    }
    View Code

     

  • 相关阅读:
    [poj_3469]多核CPU
    割点与桥,强连通分量,点双,边双[poj_1236]学校网络
    Iview 启动报错 TypeError [ERR_INVALID_CALLBACK]: Callback must be a function
    修改JAVA_HOME失效
    命令模式
    gradle implementation runtimeOnly 和api 区别
    spring boot 整合 RabbitMQ 错误
    关于技术的想法
    eclipse 背景绿豆沙颜色
    抽象工厂模式
  • 原文地址:https://www.cnblogs.com/betablewaloot/p/12489901.html
Copyright © 2020-2023  润新知