CDQ分治学习笔记

今天学了一下cdq分治,感觉这东西真的挺好用的,赶紧写点东西怕以后再忘咯
其实类似于cdq分治的东西在oi早期学排序的时候就应该学过,那就是归并排序;
归并排序的原理和cdq分治大体一样,先划分成两个区间,递归解决两边,再合并起来;
并且用归并排序求逆序对的时候本质上就是在解决一个二维偏序的问题;
首先回忆一下归并排序是怎么求逆序对的;
(我太弱了不大好描述。。。归并排序相关网上找找就好了);
这其中就利用了已经排好序的一半区间来更新另一半区间的答案,这就是cdq分治的主要思想;
当要解决的问题有时间先后时(例如 dp,n维偏序),并且后边的答案不会影响前边的答案,就可以采用cdq分治;
有一类很经典的三维偏序问题,首先可以考虑树套树解决,但那样太难写;
在求逆序对的时候有一种树状数组做法,同样有很优秀的时空复杂度,所以考虑将cdq分治和树状数组结合起来;
cdq分治中数列的顺序是第一维,每个位置上的值是第二维,再将每个位置上加一个数,其在权值树状数组中的先后顺序是第三维;
当前两维都满足条件时,就调用树状数组看第三维是否满足;
然后归并的时候将第二维上的值按顺序排好,保证更大的区间合并时两边的小区间的前两维已经有序,这样在cdq分治nlog的基础上在加一个log就可以了;
还有一种cdq分治的应用是动态维护凸包;
一般能用单调队列的斜率优化dp题,必须要都满足每个点的横坐标单调并且查找的斜率单调,当其中一个条件不满足时就能用cdq分治解决,而两个条件都不满足时要多个二分,时间上多个log;
当一个条件不满足时,先递归处理其中一半区间,算出那一半区间的值后再归并排序将不满足的条件排好序使其满足单调;
例如sdoi2012任务安排;
此题每个点横坐标因为出题人鬼畜而强行不单调;
因为dp是无后效性的,所以处理完dp值后随便你怎么排序都无所谓;
所以处理完一半的值后,顺带排好序,由于另一半还没有动,斜率单调,可以线性求出另一半如果从前一半转移值是多少,再递归处理另一半(本来想用左右区间描述的。。。但这题很鬼畜从后向前转移)
yy 了一下,如果只是斜率不单调,大概可以先递归排序好一段区间的斜率再从前边斜率是乱的但横坐标是有序的能够线性求凸包那里进行转移;

把今天做的几道题的代码贴一下吧orz

bzoj 2726 sdoi2012任务安排 cdq维护凸包

#include
#include
#include
#include
#include
#define LL long long
#define random(a,b) (a+rand()%((b)-(a)+1))
const int maxn=300005;
int n;
LL dp[maxn],f[maxn],t[maxn],s;
int id[maxn];
int tp[maxn];
int que[maxn];
double X(int x){
    return (double)t[x]; 
}
double Y(int x){
    return (double)dp[x];
}
bool check(int x,int y,int z)
{
    return (X(x)-X(y))*(Y(y)-Y(z))-(Y(x)-Y(y))*(X(y)-X(z))<0.0;  
}
double getval(int i,int j){
    return  (double)dp[j]+(double)f[i]*t[i]+(double)f[i]*s-(double)f[i]*t[j];
} 
bool vs(int x,int y,int i)
{
    return getval(i,x)-getval(i,y)<0.0;  
} 
void CDQ(int l,int r)
{
    if(l==r)return;
    int mid=(l+r)>>1;
    CDQ(mid+1,r);
    int tail=0,head=0;
    for(int i=mid+1;i<=r;i++){
        for(;tail-1>head;tail--)
            if(check(id[i],que[tail-1],que[tail-2]))break; 
        que[tail++]=id[i];
    }
    for(int k=mid;k>=l;k--){
        for(;head1;head++)
            if(vs(que[head],que[head+1],id[k]))break;
        int i=id[k];
        int j=que[head];
        dp[i]=std::min(dp[i],dp[j]+f[i]*t[i]+f[i]*s-f[i]*t[j]);
    }
    CDQ(l,mid);
    int p1=l,p2=mid+1;
    for(int i=l;i<=r;i++)
        if(p1>mid)tp[i]=id[p2++];
        else if(p2>r)tp[i]=id[p1++];
        else {
            if(t[id[p1]]id[p2]])tp[i]=id[p1++];
            else tp[i]=id[p2++];
        }
    for(int i=l;i<=r;i++)
        id[i]=tp[i];
}
int main()
{
    scanf("%d%lld",&n,&s);
    for(int i=1;i<=n;i++)scanf("%lld%lld",&t[i],&f[i]);
    for(int i=n;i>=1;i--)t[i]+=t[i+1],f[i]+=f[i+1],dp[i]=f[i]*(t[i]+s),id[i]=i;
    CDQ(1,n);
    printf("%lld\n",dp[1]);
    return 0;
}

bzoj 2683 简单题 三维偏序

#include
#include
#include
#include
#include
#define LL long long
#define random(a,b) (a+rand()%((b)-(a)+1))
int n;
const int maxn=200005;
struct asd
{
    int x,y,check,val;  
    bool operator <(const asd&p)const{
        if(x!=p.x)return xx;
        else return y<=p.y;
    }
}q[maxn*4];
int anstot=0;
int xa,ya,xb,yb;
int ans[maxn];
asd tp[maxn*4];
int a[maxn*3];
int lowbit(int x)
{
    return x&(-x);
}
void add(int pos,int v)
{
    for(int i=pos;i<=n;i+=lowbit(i))a[i]+=v;
}
int query(int pos)
{
    int tmpans=0;
    for(int i=pos;i;i-=lowbit(i))tmpans+=a[i];
    return tmpans;
}
void CDQ(int l,int r)
{
    if(l==r)return;
    int mid=(l+r)>>1;
    CDQ(l,mid);
    CDQ(mid+1,r);
    int p1=l,p2=mid+1,num=0;
    while(p1<=mid&&p2<=r){
        if(q[p1]<q[p2]){
            if(q[p1].check==1)
                add(q[p1].y,q[p1].val);
            tp[++num]=q[p1++];
        }
        else{
            if(q[p2].check!=1){
                int tans=query(q[p2].y);
                ans[q[p2].val]+=tans*(q[p2].check==2 ? 1 : -1);
            }
            tp[++num]=q[p2++];
        }
    }
    while(p1<=mid){
        if(q[p1].check==1)
            add(q[p1].y,q[p1].val);
        tp[++num]=q[p1++];
    }
    while(p2<=r){
        if(q[p2].check!=1){
            int tans=query(q[p2].y);
            ans[q[p2].val]+=tans*(q[p2].check==2 ? 1 : -1);
        }
        tp[++num]=q[p2++];
    }
    for(int i=l;i<=mid;i++){
        if(q[i].check==1)
            add(q[i].y,-q[i].val);
    }
    int tpn=0;
    for(int i=l;i<=r;i++){
        q[i]=tp[++tpn];
    }   
} 
int main()
{
    scanf("%d",&n);
    int tot=0;
    for(tot=1;;tot++)
    {
        scanf("%d",&q[tot].check);
        if(q[tot].check==1){
            scanf("%d%d%d",&q[tot].x,&q[tot].y,&q[tot].val);
        }
        else if (q[tot].check==2){
            anstot++;
            scanf("%d%d%d%d",&xa,&ya,&xb,&yb);
            q[tot].x=xb,q[tot].y=yb,q[tot].val=anstot;
            q[++tot].x=xa-1,q[tot].y=yb,q[tot].check=3,q[tot].val=anstot;
            q[++tot].x=xb,q[tot].y=ya-1,q[tot].check=3,q[tot].val=anstot;
            q[++tot].x=xa-1,q[tot].y=ya-1,q[tot].check=2,q[tot].val=anstot;
        }
        else break;
    }
    tot--;
    CDQ(1,tot);
    for(int i=1;i<=anstot;i++)printf("%d\n",ans[i]);
    return 0;
}

bzoj 3262 陌上花开 三维偏序

#include
#include
#include
#include
#include
#define LL long long
#define random(a,b) (a+rand()%((b)-(a)+1))
const int maxn=100005;
struct asd{
    int si,ci,mi;
    int ans;
    bool operator < (const asd &p)const{
        if(ci!=p.ci)return cielse return mi<=p.mi;
    }
    bool operator ==(const asd &p)const{
        return si==p.si&&ci==p.ci&&mi==p.mi;
    } 
}q[maxn],tp[maxn];
int n,k,rans[maxn];
int a[maxn*2];
int lowbit(int x)
{
    return x&(-x);
} 
void add(int pos,int v)
{
    for(int i=pos;i<=k;i+=lowbit(i))a[i]+=v;
}
int query(int pos)
{
    int tmpans=0;
    for(int i=pos;i;i-=lowbit(i))tmpans+=a[i];
    return tmpans;
}
void CDQ(int l,int r)
{
    if(l==r)return;
    int mid=(l+r)>>1;
    CDQ(l,mid);
    CDQ(mid+1,r);
    int num=0;
    int p1=l,p2=mid+1;
    while(p1<=mid&&p2<=r)
    {
        if(q[p1]<q[p2]){
            add(q[p1].mi,1);
            tp[++num]=q[p1];
            p1++;
        }
        else{
            q[p2].ans+=query(q[p2].mi);
            tp[++num]=q[p2];
            p2++; 
        }
    }
    while(p1<=mid){
        add(q[p1].mi,1);
        tp[++num]=q[p1];
        p1++;
    }
    while(p2<=r){
        q[p2].ans+=query(q[p2].mi);
        tp[++num]=q[p2];
        p2++; 
    }
    int tot=0;
    for(int i=l;i<=mid;i++)add(q[i].mi,-1);
    for(int i=l;i<=r;i++)
        q[i]=tp[++tot];
}
bool cmp(asd x,asd y){
    if(x.si!=y.si)return x.si<y.si;
    else if(x.ci!=y.ci)return x.ci<y.ci;
    else return x.mi<y.mi;
} 
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&q[i].si,&q[i].ci,&q[i].mi);
    std::sort(q+1,q+n+1,cmp); 
    CDQ(1,n);
    int nowans=0,num=0;
    for(int i=1;i<=n;i++)
    {
        num++;
        nowans=std::max(nowans,q[i].ans);
        if(!(q[i]==q[i+1])){
            rans[nowans]+=num;
            num=0,nowans=0;
        }       
    }
    for(int i=0;iprintf("%d\n",rans[i]);
    return 0;
} 

在bz排名还是比较靠前的嘛。。。所以我代码习惯应该还不是太差orz

你可能感兴趣的:(dp,斜率优化,学习心得,刷题记录,CDQ分治)