码迷,mamicode.com
首页 > 其他好文 > 详细

CDQ分治与整体二分总结

时间:2015-09-18 23:17:42      阅读:347      评论:0      收藏:0      [点我收藏+]

标签:

     Cdq分治和整体二分是两个很奇妙的东西。他们都是通过离线的思想来进行优化,从而更快的求出解。

  整体二分通俗的讲就是二分答案,但是它了不起的地方是一下子把所有的答案都二分出来了,从而可以一下子得出所有查询。

  CDQ分治通俗的讲就是二分查询。通常的做法是把所有的查询分成两半,然后通过递归先计算出左边一半的所有的查询,然后通过这些已知的左半边的值来更新右半边的值。这里,最最重要的思想是通过左半边来更新右半边。更具体一点,就是用左半边的修改来更新右半边的查询。

重要的事情说话三遍:

  CDQ分治就是通过左半边的修改来更新右半边的查询

  CDQ分治就是通过左半边的修改来更新右半边的查询!

  CDQ分治就是通过左半边的修改来更新右半边的查询

      CDQ的主要作用就是降维。因为当你二分查询的时候,你可以保证左半边的查询都满足你二分前的顺序。

 

一、整体二分

1. 最经典的题目:带修改的区间第k小数(hdu5412

因为相同的题目太多了,挑了一道比较新的题。其实就是题面不一样。

         题目意思:

         给你N个数,有两种询问。一个是修改第K个数的值,二是询问一个区间内的第k小的数,输出这个数。

         所以,具体的做法就是二分答案。如果比猜测的数字小的个数比较多,那么答案一定在左边的区间内,反之就在右边的区间内。然后递归来继续这个操作。

   这是写的第一道整体二分,不要问我为什么和网上的代码几乎一模一样。我只是因为看了很久才看懂,所以几乎就背出来了……

代码:

技术分享
#include <iostream>
#include <sstream>
#include <ios>
#include <iomanip>
#include <functional>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <climits>
#include <cctype>
#define INF 0x3f3f3f3f
#define MP(X,Y) make_pair(X,Y)
#define PB(X) push_back(X)
#define REP(X,N) for(int X=0;X<N;X++)
#define REP2(X,L,R) for(int X=L;X<=R;X++)
#define DEP(X,R,L) for(int X=R;X>=L;X--)
#define CLR(A,X) memset(A,X,sizeof(A))
#define IT iterator
#define M_PI 3.14159265358979323846
#define _ ios_base::sync_with_stdio(0);cin.tie(0);
#define X first
#define Y second
#define MAX_V 10101
#define maxn 323400
#define lowbit(X) (X & (-X))
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
typedef pair<PII,int>PPI;


int dat[maxn];
void add(int k,int val){
    while(k<maxn){
        dat[k]+=val;
        k+=lowbit(k);
    }
}
int query(int k){
    int sum=0;
    while(k>0){
        sum+=dat[k];
        k-=lowbit(k);
    }
    return sum;
}

struct {
    //sign x query val k
    int s,x,val,q,k,l,r,cur;
}q[maxn],q1[maxn],q2[maxn];
int A[maxn];
int temp[maxn];
int ans[maxn];
//×ó±ÕÓÒ±Õ 
void div_solve(int head,int tail,int l,int r){
    //cout<<head<<‘ ‘<<tail<<‘ ‘<<l<<‘ ‘<<r<<endl;
    if(head>tail)return ;
    if(l==r){
        for(int i=head;i<=tail;++i){
            if(q[i].s==3)
                ans[q[i].q]=l;
        }
        return ;
    }
    int mid=l+r>>1;
    for(int i=head;i<=tail;++i){
        if(q[i].s==1 && q[i].val<=mid) add(q[i].x,1); 
        else if(q[i].s==2 && q[i].val<=mid) add(q[i].x,-1);
        else if(q[i].s==3) temp[i]=query(q[i].r)-query(q[i].l-1);     
    }
    for(int i=head;i<=tail;++i){
        if(q[i].s==1 && q[i].val<=mid) add(q[i].x,-1); 
        else if(q[i].s==2 && q[i].val<=mid) add(q[i].x,1);
    }
    int l1=0,l2=0;
    for(int i=head;i<=tail;++i){
        if(q[i].s==3){
            if(q[i].cur+temp[i]>=q[i].k){
                q1[l1++]=q[i];    
            }
            else{
                q[i].cur+=temp[i];
                q2[l2++]=q[i];
            }
        }
        else
        {
            if(q[i].val<=mid){
                q1[l1++]=q[i];
            }
            else{
                q2[l2++]=q[i];
            }
        }
    }
    for(int i=0;i<l1;++i){
        q[i+head]=q1[i];    
    }
    for(int i=0;i<l2;++i){
        q[i+head+l1]=q2[i];
    }
    div_solve(head,head+l1-1,l,mid);
    div_solve(head+l1,tail,mid+1,r);
}

int main()
{
    int n;
    while(~scanf("%d",&n)){
        int a,b,c,d;
        int cnt=0;
        for(int i=1;i<=n;++i){
            scanf("%d",&a);
            A[i]=a;
            q[cnt].s=1;q[cnt].x=i;
            q[cnt].cur=0;
            q[cnt++].val=a;
        }
        int t,tt=0;
        scanf("%d",&t);
        for(int i=1;i<=t;++i){
            scanf("%d",&a);
            if(a==1){
                scanf("%d%d",&b,&c);
                q[cnt].s=2;q[cnt].x=b;q[cnt].cur=0;
                q[cnt++].val=A[b];
                q[cnt].s=1;q[cnt].x=b;q[cnt].cur=0;
                q[cnt++].val=c;
                A[b]=c;
            }
            else{
                scanf("%d%d%d",&a,&b,&c);
                q[cnt].s=3;q[cnt].l=a;q[cnt].q=++tt;
                q[cnt].cur=0;
                q[cnt].r=b;q[cnt++].k=c;
            }
        }
        div_solve(0,cnt-1,0,INF);
        for(int i=1;i<=tt;++i)
            printf("%d\n",ans[i]);                
    }
    return 0;
}
View Code

 

2.POI 2011 Meteors

//因为bzoj现在都要收费了,找了半天终于找到免费的。

http://main.edu.pl/en/archive/oi/18/met

题目意思:

一个行星有一条圆形的轨道。这条轨道被分成了m份,这些轨道分别属于n个国家。一个国家可以拥有多个轨道。在轨道周围会有流星雨落下,现在各个国家需要收集一些流星雨。问你需要经过多少时间,各个国家才能完成他们的收集任务。

因为问的是时间,所以第一个想到是二分时间。接下来在维护一个线段树,用来区间修改。然后再根据二分时间,检查是否满足要求。继续二分,直到找到解。其中的一个处理是在t+1的时间再加一个无限大的流星,用来确定是不是能够成功收集。

代码:

 

技术分享
#include <iostream>
#include <sstream>
#include <ios>
#include <iomanip>
#include <functional>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <climits>
#include <bitset>
#include <cctype>
#define INF 0x3f3f3f3f
#define MP(X,Y) make_pair(X,Y)
#define PB(X) push_back(X)
#define REP(X,N) for(int X=0;X<N;X++)
#define REP2(X,L,R) for(int X=L;X<=R;X++)
#define DEP(X,R,L) for(int X=R;X>=L;X--)
#define CLR(A,X) memset(A,X,sizeof(A))
#define IT iterator
#define M_PI 3.14159265358979323846
#define _ ios_base::sync_with_stdio(0);cin.tie(0);
#define X first
#define Y second
#define MAX_V 10101
#define maxn 301234
#define lowbit(X) (X & (-X))
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
typedef pair<PII,int>PPI;
const ll INF2=(0x3f3f3f3f)*100ll;

//bit Çø¼äÐÞ¸Ä µ¥µã²éѯ 
ll dat[maxn];
//×ó±ÕÓÒ¿ª

void init(){
    memset(dat,0,sizeof(dat));
}
void add(int l,int r,int val){
    while(l<maxn){
        dat[l]+=val;
        l+=lowbit(l);
    }
    while(r<maxn){
        dat[r]-=val;
        r+=lowbit(r);
    }
}
ll query(int k){
    ll sum=0;
    while(k>0){
        sum+=dat[k];
        k-=lowbit(k);
    }
    return sum;
}

struct edge{
    int to,next;
}G[maxn];
int head[maxn];
void add_edge(int from,int to,int &cnt){
    edge e;
    e.to=to;e.next=head[from];
    G[++cnt]=e;
    head[from]=cnt;
}

struct node{
    int l,r,num;
}Q[maxn];
ll cur[maxn];
int A[maxn],ID[maxn],T[maxn],Ans[maxn];
int n,m,t;
int temp[maxn],temp2[maxn];

void solve(int start,int tail,int l,int r){
    if(start>tail) return ;
    if(l==r){
        for(int i=start;i<=tail;++i){
            int d=ID[i];
            Ans[d]=l;
        }
        return ;
    }
    //init();
    int mid=(l*1ll+r)>>1;
    for(int i=l;i<=mid;++i){
        int l=Q[i].l,r=Q[i].r,num=Q[i].num;
        if(l>r){
            add(l,m+1,num);
            add(1,r+1,num);
        }
        else
            add(l,r+1,num);
    }
    int l1=0,l2=0;
    for(int i=start;i<=tail;++i){
        int d=ID[i];
        ll cnt=0;
        for(int j=head[d];j;j=G[j].next){
            int to=G[j].to;
            cnt+=query(to);
            if(cnt>INF)break;
        }
        if(cnt+cur[d]>=T[d]){
            temp[l1++]=d;
        }
        else{
            temp2[l2++]=d;
            cur[d]+=cnt;
        }
    }
    for(int i=0;i<l1;++i){
        ID[start+i]=temp[i];
    }
    for(int i=0;i<l2;++i){
        ID[start+l1+i]=temp2[i];
    }
    for(int i=l;i<=mid;++i){
        int l=Q[i].l,r=Q[i].r,num=Q[i].num;
        if(l>r){
            add(l,m+1,-num);
            add(1,r+1,-num);
        }
        else
            add(l,r+1,-num);
    }
    //cout<<l<<" "<<r<<endl;
    solve(start,start+l1-1,l,mid);
    solve(start+l1,tail,mid+1,r);
}

int Scan(){
    int res=0, ch;
    ch=getchar();
    //windows»Ø³µ\r\n  linux»Ø³µ\n  ·ÀÖ¹Êý¾ÝÖÐûÓÐÈ¥³ý\r
    if(ch==10) ch=getchar();
    if(ch>=0&&ch<=9)
        res=ch-0;
    else return -1;
    while((ch=getchar())>=0&&ch<=9)
        res=res*10+ch-0;
    return res;
}
void Out(ll a){
    if(a>9)
        Out(a/10);
    putchar(a%10+0);
}
int main()
{
    //scanf("%d%d",&n,&m);
    n=Scan();m=Scan();
    memset(head,0,sizeof(head));
    int tot=0;
    for(int i=1;i<=m;++i){
        //scanf("%d",&A[i]);
        A[i]=Scan();
        add_edge(A[i],i,tot);
    }
    for(int i=1;i<=n;++i){
        //scanf("%d",&T[i]);
        T[i]=Scan();
        ID[i]=i;
    }
    //scanf("%d",&t);
    t=Scan();
    int l,r,num;
    for(int i=1;i<=t;++i){
        //scanf("%d%d%d",&l,&r,&num);
        l=Scan();r=Scan();num=Scan();
        Q[i].l=l;Q[i].r=r;
        Q[i].num=num;
    }
    t+=1;
    Q[t].num=INF;Q[t].l=1;Q[t].r=m;
    //memset(cur,0,sizeof(cur));
    solve(1,n,1,t);
    for(int i=1;i<=n;++i){
        if(Ans[i]==t){
            puts("NIE");
        }
        else{
            //printf("%d\n",Ans[i]);
            Out(Ans[i]);
            putchar(\n);
        }
    }
    
    return 0;
}
View Code

 

其他学习资料:http://www.cnblogs.com/zig-zag/archive/2013/04/18/3027707.html

二、CDQ分治

1.Boring Class(hdu5324)

         题目意思:给你两个序列,分别为L序列和R序列。求一个最长的序列,满足对于每个Vi,Vj,L[Vi]>=L[Vj] 并且R[Vi]<=R[Vj],同时还要的是字典序最小这个条件。

         这题是典型的CDQ分治的题目。因为有三维,顺序维、L维和R维,我们不好处理。所以这个时候就可以通过CDQ分治来二分它的顺序。这样就可以保证在CDQ的过程中,时间顺序一定是满足的。接下来在CDQ中,我们不妨按照R来排序,这样可以确定R也满足顺序,然后再进行DP就可以得出结果了。其中要注意的是从右往左DP,这样可以求出字典序的最小。

         代码:

技术分享
#include <iostream>
#include <sstream>
#include <ios>
#include <iomanip>
#include <functional>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <climits>
#include <bitset>
#include<cassert>
#include <cctype>
#define NINF -0x3f3f3f3f
#define MP(X,Y) make_pair(X,Y)
#define PB(X) push_back(X)
#define REP(X,N) for(int X=0;X<N;X++)
#define REP2(X,L,R) for(int X=L;X<=R;X++)
#define DEP(X,R,L) for(int X=R;X>=L;X--)
#define CLR(A,X) memset(A,X,sizeof(A))
#define IT iterator
#define M_PI 3.14159265358979323846
#define _ ios_base::sync_with_stdio(0);cin.tie(0);
#define X first
#define Y second
#define MAX_V 10101
#define maxn 50099
#define lowbit(X) (X & (-X))
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
typedef pair<PII,int>PPI;


int tot;
int L[maxn],R[maxn];
int Num[maxn*2],D[maxn];
struct node{
    int l,r,id;
    bool operator<(const node &B)const{
        if(r!=B.r)return r<B.r;
        if(l!=B.l)return l>B.l;
        return id<B.id;
    }
}q1[maxn],q2[maxn];
int dat[maxn*2];
void update(int k,int val){
    while(k<tot+10){
        dat[k]=max(dat[k],val);
        k+=lowbit(k);
    }
}
void clear(int k){
    while(k<tot+10){
        dat[k]=0;
        k+=lowbit(k);
    }
}
int query(int k){
    int Max=0;
    while(k>0){
        Max=max(Max,dat[k]);
        k-=lowbit(k);
    }
    return Max;
}


void cdq(int l,int r){
    if(l==r)return;
    int mid=(l+r)/2;
    cdq(mid+1,r);
    for(int i=l;i<=mid;++i){
        q1[i].l=L[i];q1[i].r=R[i];q1[i].id=i;    
    }
    for(int i=mid+1;i<=r;++i){
        q2[i].l=L[i];q2[i].r=R[i];q2[i].id=i;    
    }
    sort(q1+l,q1+mid+1);
    sort(q2+mid+1,q2+r+1);
    for(int i=mid,j=r;i>=l;--i){
        while(j>mid && q2[j].r>=q1[i].r){
            update(q2[j].l,D[q2[j].id]);
            j--;
        }
        D[q1[i].id]=max(D[q1[i].id],query(q1[i].l)+1);
    }
    for(int i=mid+1;i<=r;++i){
        clear(q2[i].l);
    }
    cdq(l,mid);    
}

int Scan(){
    int res=0, ch;
    ch=getchar();
    //windows»Ø³µ\r\n  linux»Ø³µ\n  ·ÀÖ¹Êý¾ÝÖÐûÓÐÈ¥³ý\r
    if(ch==10) ch=getchar();
    if(ch>=0&&ch<=9)
        res=ch-0;
    else return -1;
    while((ch=getchar())>=0&&ch<=9)
        res=res*10+ch-0;
    return res;
}
void Out(ll a){
    if(a>9)
        Out(a/10);
    putchar(a%10+0);
}


int main()
{
    int n;
    //~(n=Scan())
    //freopen("data.in","r",stdin);
    while(~scanf("%d",&n)){
        tot=0;
        for(int i=1;i<=n;++i){
            scanf("%d",&L[i]);
            //L[i]=Scan();
            Num[tot++]=L[i];
        }
        for(int i=1;i<=n;++i){
            scanf("%d",&R[i]);
            //R[i]=Scan();
            Num[tot++]=R[i];
        }
        sort(Num,Num+tot);
        tot=unique(Num,Num+tot)-Num;
        for(int i=1;i<=n;++i){
            L[i]=(lower_bound(Num,Num+tot,L[i])-Num)+1;
            R[i]=(lower_bound(Num,Num+tot,R[i])-Num)+1;    
        }
    //    cout<<"ok"<<endl;
        for(int i=1;i<maxn;++i)
            D[i]=1;
        cdq(1,n);
        int ans=0;
        for(int i=1;i<=n;++i){
            ans=max(D[i],ans);
        }
        printf("%d\n",ans);
        //Out(ans);putchar(‘\n‘);
        int pre=0;
        for(int i=1;i<=n;++i){
            if(D[i]==ans && (pre==0 || ( L[pre]>=L[i] && R[pre]<=R[i]))){
                ans--;
                if(pre)putchar( );
                printf("%d",i);    
                //Out(i);
                pre=i;
            }
        }
        putchar(\n);
    }
    return 0;
}
View Code

 

2. Pinball Game 3Dhdu4742

         题目意思:给你三个序列X,Y,Z求最长上升序列的长度。同时输出可以构成最长上升序列的方法数。

         和之前的一题几乎一样,多了一个维护方法数。所以树状数组不仅要维护一个Dp的值,同时还要维护一个方法数。如果Dp更新了,方法数就是更新它的值,反之,在原来的基础上要加上那种方法的个数。

         代码:

技术分享
#include <iostream>
#include <sstream>
#include <ios>
#include <iomanip>
#include <functional>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <climits>
#include <bitset>
#include <cctype>
#define NINF -0x3f3f3f3f
#define MP(X,Y) make_pair(X,Y)
#define PB(X) push_back(X)
#define REP(X,N) for(int X=0;X<N;X++)
#define REP2(X,L,R) for(int X=L;X<=R;X++)
#define DEP(X,R,L) for(int X=R;X>=L;X--)
#define CLR(A,X) memset(A,X,sizeof(A))
#define IT iterator
#define M_PI 3.14159265358979323846
#define _ ios_base::sync_with_stdio(0);cin.tie(0);
#define X first
#define Y second
#define MAX_V 10101
#define maxn 100009
#define lowbit(X) (X & (-X))
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,ll>PII;
typedef pair<PII,int>PPI;

int Fu[maxn*3];
ll Num[maxn];
int D[maxn];
const ll MOD=(1<<30);
struct node{
    int x,y,z;
    bool operator <(const node&B) const{
        if(x!=B.x) return x<B.x;
        if(y!=B.y) return y<B.y;
        return z<B.z;
    }
}A[maxn];
struct node2{
    int x,y,z,id;
    bool operator<(const node2&B)const{
        if(y!=B.y) return y<B.y;
        if(z!=B.z) return z<B.z;
        return id<B.id;
    }
}q1[maxn],q2[maxn];
PII dat[maxn*3];
void update(int k,int val,ll num){
    while(k<maxn*3){
        if(val>dat[k].X){
            dat[k].X=val;
            dat[k].Y=num;
        }
        else if(val==dat[k].X){
            dat[k].Y+=num;
            dat[k].Y%=MOD;
        }
        k+=lowbit(k);
    }
}
PII  query(int k){
    PII Max=MP(0,0);
    while(k>0){
        if(dat[k].X>Max.X){
            Max=dat[k];
        }
        else if(dat[k].X==Max.X){
            Max.Y+=dat[k].Y;
            Max.Y%=MOD;
        }
        k-=lowbit(k);
    }
    return Max;
}
void clear(int k){
    while(k<maxn*3){
        dat[k]=MP(0,0);
        k+=lowbit(k);
    }
}
void cdq(int l,int r){
    if(l==r)return ;
    int mid=l+r>>1;
    cdq(l,mid);
    for(int i=l;i<=mid;++i){
        q1[i].x=A[i].x;q1[i].y=A[i].y;
        q1[i].z=A[i].z;q1[i].id=i;
    }
    for(int i=mid+1;i<=r;++i){
        q2[i].x=A[i].x;q2[i].y=A[i].y;
        q2[i].z=A[i].z;q2[i].id=i;
    }
    sort(q1+l,q1+mid+1);
    sort(q2+mid+1,q2+r+1);
    for(int i=mid+1,j=l;i<=r;++i){
        while(j<=mid && q1[j].y<=q2[i].y){
            update(q1[j].z,D[q1[j].id],Num[q1[j].id]);
            j++;
        }
        PII temp=query(q2[i].z);
        if(D[q2[i].id]<temp.X+1){
            D[q2[i].id]=temp.X+1;
            Num[q2[i].id]=temp.Y;
        }
        else if(D[q2[i].id]==temp.X+1){
            Num[q2[i].id]+=temp.Y;
            Num[q2[i].id]%=MOD;
        }
    }
    for(int i=l;i<=mid;++i){
        clear(q1[i].z);
    }
    cdq(mid+1,r);
}

int Scan(){
    int res=0, ch;
    ch=getchar();
    //windows»Ø³µ\r\n  linux»Ø³µ\n  ·ÀÖ¹Êý¾ÝÖÐûÓÐÈ¥³ý\r
    if(ch==10) ch=getchar();
    if(ch>=0&&ch<=9)
        res=ch-0;
    else return -1;
    while((ch=getchar())>=0&&ch<=9)
        res=res*10+ch-0;
    return res;
}
void Out(ll a){
    if(a>9)
        Out(a/10);
    putchar(a%10+0);
}
int main()
{
    int T;
    //scanf("%d",&T);
    T=Scan();
    while(T--){
        int n;
        //scanf("%d",&n);
        n=Scan();
        int tot=0;
        for(int i=1;i<=n;++i){
            //scanf("%d%d%d",&A[i].x,&A[i].y,&A[i].z);
            A[i].x=Scan();A[i].y=Scan();A[i].z=Scan();
            Fu[tot++]=A[i].x;
            Fu[tot++]=A[i].y;
            Fu[tot++]=A[i].z;
        }
        sort(Fu,Fu+tot);
        for(int i=1;i<=n;++i){
            D[i]=Num[i]=1;
            A[i].x=lower_bound(Fu,Fu+tot,A[i].x)-Fu+1;
            A[i].y=lower_bound(Fu,Fu+tot,A[i].y)-Fu+1;
            A[i].z=lower_bound(Fu,Fu+tot,A[i].z)-Fu+1;
        }
        sort(A+1,A+n+1);
        cdq(1,n);
        int Max=0;
        for(int i=1;i<=n;++i){
            Max=max(Max,D[i]);            
        }
        ll sum=0;
        for(int i=1;i<=n;++i){
            if(D[i]==Max){
                sum=sum+Num[i];
                sum%=MOD;
            }
        }
        printf("%d %I64d\n",Max,sum);
    }
    return 0;
}
View Code

 

3. Machine Works(hdu3842)

         这是2011WorldFinal的一道题目,所以做的人不多。要是我当时看到是WorldFinal的题,肯定吓得直接跳过了。但是事实上难度还可以。

         题目意思:

         初始时,给你一笔资金C元,以及一段时间D天。在其中的某天会提供给你购买机器的机会,你可以选择购买或者不购买。买机器需要花费P元,卖出可以获得R元,从够买机器的第二天起,到卖出机器的前一天,你可以每天获得G元。最后一天将卖出所有机器。同时,由于场地限制,最多只能同时拥有一台机器。

         首先我们可以用D[i]来维护前一个状态结束,刚刚到达i这个机器时,所能拥有的最大值。

            D[i]=max{D[j]-P[j]+R[j]+G[j]*(day[i]-day[j]-1)}

         因为j的信息都是已知的,所以可以化解为

            D[i]=C+k*day[i];

         所以也就化解为斜率优化DP的问题了。

         所以我们接下来按照斜率从小到大来排序,用一个队列来维护。队列中的元素满足k是递增的。所以队尾的元素一定在某些x时候会比队尾的前一个元素大。现在当你要新加入一个元素到末尾的时候,新加入的元素可能无论在什么时候都比原先在队尾的元素大,这样就删掉那个元素。

         同时对于队首的维护,当目前的x不是最大时,删除这个队首的值。DP一下就可以了。

         代码:

技术分享
#include <iostream>
#include <sstream>
#include <ios>
#include <iomanip>
#include <functional>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <climits>
#include <bitset>
#include <cctype>
#define NINF -0x3f3f3f3f
#define MP(X,Y) make_pair(X,Y)
#define PB(X) push_back(X)
#define REP(X,N) for(int X=0;X<N;X++)
#define REP2(X,L,R) for(int X=L;X<=R;X++)
#define DEP(X,R,L) for(int X=R;X>=L;X--)
#define CLR(A,X) memset(A,X,sizeof(A))
#define IT iterator
#define M_PI 3.14159265358979323846
#define _ ios_base::sync_with_stdio(0);cin.tie(0);
#define X first
#define Y second
#define MAX_V 10101
#define maxn 123456
#define lowbit(X) (X & (-X))
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
typedef pair<PII,int>PPI;
//19:52


ll D[maxn];
struct node{
    int day,pri,ret,val,id;
    bool operator <(const node &B)const {
        return day<B.day;
    }
}A[maxn];
struct node2{
    int day,pri,ret,val,id;
    bool operator <(const node2 &B)const {
        if(val==B.val){
            ll t1=D[id]+ret-pri-1ll*(day+1)*val;
            ll t2=D[B.id]+B.ret-B.pri-1ll*(B.day+1)*B.val;
            return t1<t2;
        }
        return val<B.val;
    }
}q1[maxn],q2[maxn];
ll K[maxn],B[maxn];
ll f(int i,int x){
    ll ans=K[i]*x+B[i];
    return ans;
}
bool check(int i1,int i2,ll k3,ll b3){
    ll k1=K[i1],b1=B[i1],k2=K[i2],b2=B[i2];
    return 1.0*(k3-k2)*(b1-b3)-1.0*(b2-b3)*(k3-k1)<0;    
    //return (double)(k2-k1)*(b3-b1)-(double)(k3-k1)*(b2-b1)<0;            
}
void cdq(int l,int r){
    if(l==r)return ;
    int mid=l+r>>1;
    cdq(l,mid);
    for(int i=l;i<=mid;++i){
        q1[i].day=A[i].day;q1[i].pri=A[i].pri;
        q1[i].ret=A[i].ret;q1[i].val=A[i].val;
        q1[i].id=A[i].id;
    }
    for(int i=mid+1;i<=r;++i){
        q2[i].day=A[i].day;q2[i].pri=A[i].pri;
        q2[i].ret=A[i].ret;q2[i].val=A[i].val;
        q2[i].id=A[i].id;
    }
    sort(q1+l,q1+mid+1);
    int head=0,tail=-1;
    for(int i=l;i<=mid;++i){
        if(D[q1[i].id]<q1[i].pri)continue;
        ll t1=q1[i].val;
        ll t2=D[q1[i].id]+q1[i].ret-q1[i].pri-1ll*(q1[i].day+1)*q1[i].val;
        
//        if(tail-head+1<2){
//            K[++tail]=t1;
//            B[tail]=t2;
//            //if(head-tail==1 && K[head]==K[tail] && B[head]<=B[tail])head++;
//        }
//        else{
        
            while(tail>head && !check(tail-1,tail,t1,t2))tail--;
            K[++tail]=t1;
            B[tail]=t2;
        //}
    }
    for(int i=mid+1;i<=r;++i){
        int d=q2[i].day;
        while(head<tail && f(head,d)<=f(head+1,d))head++;    
        ll temp=f(head,d);
        if(temp<0)continue;
        D[q2[i].id]=max(temp,D[q2[i].id]);
    }
    cdq(mid+1,r);
}

int main()
{
    int n,c,d;
    int tt=0;
    while(~scanf("%d%d%d",&n,&c,&d)){
        if(n==0 && c==0 && d==0)break;
        D[0]=c;
        A[0].id=0;A[0].day=0;A[0].val=0;A[0].pri=0;A[0].ret=0;
        for(int i=1;i<=n;++i){
            scanf("%d%d%d%d",&A[i].day,&A[i].pri,&A[i].ret,&A[i].val);
            A[i].id=i;
            D[i]=c;
        }
        A[n+1].day=d+1;A[n+1].id=n+1;
        A[n+1].pri=A[n+1].val=A[n+1].ret=0;
        D[n+1]=c;
        sort(A+1,A+n+1);
        cdq(0,n+1);
        printf("Case %d: %I64d\n",++tt,D[n+1]);
    }
    return 0;
}
View Code

 

4. 动态逆序对(BZOJ3295)

         题目意思:给你一个序列求每次删除一个元素之前的逆序对的个数。

         假设你要删除一个元素,那么减少的逆序对的个数不仅和他前面位置的有关,还和他后面位置的数有关。所以假设已经知道在最初状态时在他前面和后面分别的逆序对数。在删除的时候,更新这两个值,问题就解决了。

         至于逆序对的个数,预处理一下就可以了。

   代码:

技术分享
#include <iostream>
#include <sstream>
#include <ios>
#include <iomanip>
#include <functional>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <climits>
#include <bitset>
#include <cctype>
#define NINF -0x3f3f3f3f
#define MP(X,Y) make_pair(X,Y)
#define PB(X) push_back(X)
#define REP(X,N) for(int X=0;X<N;X++)
#define REP2(X,L,R) for(int X=L;X<=R;X++)
#define DEP(X,R,L) for(int X=R;X>=L;X--)
#define CLR(A,X) memset(A,X,sizeof(A))
#define IT iterator
#define M_PI 3.14159265358979323846
#define _ ios_base::sync_with_stdio(0);cin.tie(0);
#define X first
#define Y second
#define MAX_V 10101
#define maxn 123456
#define lowbit(X) (X & (-X))
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
typedef pair<PII,int>PPI;

ll dat[maxn];
void update(int k,int val){
    while(k<maxn){
        dat[k]+=val;
        k+=lowbit(k);
    }
}
ll query(int k){
    ll sum=0;
    while(k>0){
        sum+=dat[k];
        k-=lowbit(k);    
    }
    return sum;
}
int A[maxn];
int Pos[maxn];
ll L[maxn],R[maxn];
struct node{
    int val,id;
    bool operator <(const node &B)const {
        return Pos[val]<Pos[B.val];
    }
}Q[maxn],q1[maxn],q2[maxn];
ll Ans[maxn];
int n,m;
void solve(int l,int r){
    if(l==r){
    //    cout<<Pos[Q[l].val]<<" "<<L[Pos[Q[l].val]]<<" "<<R[Pos[Q[l].val]]<<endl;
        Ans[l]=L[Pos[Q[l].val]]+R[Pos[Q[l].val]];
        return ;
    }
    int mid=l+r>>1;
    solve(l,mid);
    int l1=0,l2=0;
    for(int i=l;i<=mid;++i){
        q1[l1++]=Q[i];
    }
    for(int i=mid+1;i<=r;++i){
        q2[l2++]=Q[i];
    }
    sort(q1,q1+l1);
    sort(q2,q2+l2);
    int j=0;
    for(int i=0;i<l2;++i){
        while(j<l1 && Pos[q1[j].val]<Pos[q2[i].val]){
            update(q1[j].val,1);
            //cout<<" "<<Pos[q1[j].val]<<" "<<n<<" "<<query(n)<<endl;
            j++;
        }
        int ps=Pos[q2[i].val];
        L[ps]-=query(n)-query(q2[i].val);
    }
    for(int i=0;i<j;++i){
        update(q1[i].val,-1);
    }
    //----
    j=l1-1;
    for(int i=l2-1;i>=0;--i){
        while(j>=0 && Pos[q1[j].val]>Pos[q2[i].val]){
            update(q1[j].val,1);
            j--;
        }
        int ps=Pos[q2[i].val];
        R[ps]-=query(q2[i].val);
    }
    for(int i=l1-1;i>j;--i){
        update(q1[i].val,-1);
    }
    solve(mid+1,r);
}



int main()
{
    while(~scanf("%d%d",&n,&m)){
        memset(dat,0,sizeof(dat));
        ll sum=0;
        for(int i=1;i<=n;++i){
            scanf("%d",&A[i]);
            L[i]=query(n)-query(A[i]);
            sum+=L[i];
            update(A[i],1);
            Pos[A[i]]=i;
        }
        memset(dat,0,sizeof(dat));
        for(int i=n;i>=1;--i){
            R[i]=query(A[i]);
            update(A[i],1);
        }    
        int a;
        for(int i=1;i<=m;++i){
            scanf("%d",&a);
            Q[i].val=a;
            Q[i].id=i;
        }
        memset(dat,0,sizeof(dat));
        solve(1,m);
        for(int i=1;i<=m;++i){
            printf("%lld\n",sum);
            sum-=Ans[i];
        }
    }
    return 0;
}
View Code

 

5. Crowd(hdu4456)

         题目意思:给你一张图,有两种查询,1.是给某个点的值增加Z  2.是查询曼哈顿距离小于某个点的值的和。

         首先,需要的是坐标轴旋转。然后就可以用CDQ分治,或者是二维树状数组来搞了。

         这篇讲的很好,我就不废话了。

     http://www.aiuxian.com/article/p-2286542.html

代码:树状数组

技术分享
#include <iostream>
#include <sstream>
#include <ios>
#include <iomanip>
#include <functional>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <climits>
#include <bitset>
#include <cctype>
#define NINF -0x3f3f3f3f
#define MP(X,Y) make_pair(X,Y)
#define PB(X) push_back(X)
#define REP(X,N) for(int X=0;X<N;X++)
#define REP2(X,L,R) for(int X=L;X<=R;X++)
#define DEP(X,R,L) for(int X=R;X>=L;X--)
#define CLR(A,X) memset(A,X,sizeof(A))
#define IT iterator
#define M_PI 3.14159265358979323846
#define _ ios_base::sync_with_stdio(0);cin.tie(0);
#define X first
#define Y second
#define MAX_V 10101
#define maxn 1
#define lowbit(X) (X & (-X))
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
typedef pair<PII,int>PPI;

void change(int &x,int &y,int n){
    int t=x-y+n;
    y=x+y-1;
    x=t;
}
const int MOD=3001003;
int dat[MOD];
int num[MOD];
int Hash(int x){
    int t=x%MOD;
    while(1){
        if(num[t]==x || num[t]==-1){
            num[t]=x;
            return t;
        }
        t++;
        if(t==MOD)t=0;
    }
    return -1;
}
int gethash(int x){
    int t=x%MOD;
    while(1){
        if(num[t]==-1 || num[t]==x)return t;
        t++;
        if(t==MOD)t=0;
    }
    return -1;
}
int Bound;
void add(int x,int y,int add){
    for(int i=x;i<Bound;i+=lowbit(i)){
        for(int j=y;j<Bound;j+=lowbit(j)){
            dat[Hash(i*Bound+j)]+=add;
        }
    }
}
int getsum(int x,int y){
    int ans=0;
    for(int i=x;i>0;i-=lowbit(i)){
        for(int j=y;j>0;j-=lowbit(j)){
            ans+=dat[gethash(i*Bound+j)];
        }
    }
    return ans;
}
int query(int a,int b,int c,int d){
    int ans=0;
    a=max(a,1);b=max(b,1);
    c=min(Bound-1,c);
    d=min(Bound-1,d);
    ans=getsum(c,d)+getsum(a-1,b-1)
        -getsum(a-1,d)-getsum(c,b-1);
    return ans;
}
int main()
{
    int n,m;
    while(1){
        scanf("%d",&n);
        if(n==0)break;
        scanf("%d",&m);
        Bound=n*2+1;
        memset(dat,0,sizeof(dat));
        memset(num,-1,sizeof(num));
        int a,x,y,z;
        for(int i=0;i<m;++i){
            scanf("%d%d%d%d",&a,&x,&y,&z);
            change(x,y,n);
            if(a==1){
                add(x,y,z);
            }
            else{
                printf("%d\n",query(x-z,y-z,x+z,y+z));
            }
        }
    }
    return 0;
}
View Code

代码:CDQ

技术分享
#include <iostream>
#include <sstream>
#include <ios>
#include <iomanip>
#include <functional>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <climits>
#include <bitset>
#include <cctype>
#define NINF -0x3f3f3f3f
#define MP(X,Y) make_pair(X,Y)
#define PB(X) push_back(X)
#define REP(X,N) for(int X=0;X<N;X++)
#define REP2(X,L,R) for(int X=L;X<=R;X++)
#define DEP(X,R,L) for(int X=R;X>=L;X--)
#define CLR(A,X) memset(A,X,sizeof(A))
#define IT iterator
#define M_PI 3.14159265358979323846
#define _ ios_base::sync_with_stdio(0);cin.tie(0);
#define X first
#define Y second
#define MAX_V 9
#define maxn 81234
#define lowbit(X) (X & (-X))
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
typedef pair<PII,int>PPI;

void change(int &x,int &y,int n){
    int t=x-y+n;
    y=x+y-1;
    x=t;
}
int Bound;
struct node{
    int s,x,y,z,id;
    bool operator <(const node &B)const{
        if(x!=B.x)return x<B.x;
        return y<B.y;
    }    
}Q[maxn*4],q1[maxn*4],q2[maxn*4];

int Ans[maxn];
void addnode(int s,int x,int y,int z,int id,int &tot){
    Q[++tot].s=s;
    Q[tot].x=x;Q[tot].y=y;Q[tot].z=z;
    Q[tot].id=id;
}
int dat[maxn];
void add(int k,int val){
    while(k<Bound){
        dat[k]+=val;
        k+=lowbit(k);
    }
}
int query(int k){
    int sum=0;
    while(k>0){
        sum+=dat[k];
        k-=lowbit(k);
    }
    return sum;
}
void cdq(int l,int r){
    if(l==r)return;
    int mid=l+r>>1;
    cdq(l,mid);
    int l1=0,l2=0;
    for(int i=l;i<=mid;++i){
        q1[l1++]=Q[i];
    }
    for(int i=mid+1;i<=r;++i){
        q2[l2++]=Q[i];
    }
    sort(q1,q1+l1);
    sort(q2,q2+l2);
    int j=0;
    for(int i=0;i<l2;++i){
        if(q2[i].s==1)continue;
        while(j<l1 &&(q1[j].s==2 || q1[j].x<=q2[i].x)){
            if(q1[j].s==1)add(q1[j].y,q1[j].z);
            j++;
        }
        Ans[q2[i].id]+=query(q2[i].y)*q2[i].z;
    }
    for(int i=j-1;i>=0;--i)
    if(q1[i].s==1)add(q1[i].y,-q1[i].z);
    cdq(mid+1,r);
}

int main()
{
    int n,m;
    while(1){
        scanf("%d",&n);
        if(n==0)break;
        scanf("%d",&m);
        Bound=n*2+1;
        int s,x,y,z;
        int a,b,c,d;
        int tot=0;
        for(int i=1;i<=m;++i){
            scanf("%d%d%d%d",&s,&x,&y,&z);
            change(x,y,n);
            if(s==1){
                addnode(s,x,y,z,i,tot);
            }
            else{
                a=x-z;b=y-z;c=x+z;d=y+z;
                a=max(a,1);b=max(b,1);
                c=min(Bound-1,c);
                d=min(Bound-1,d);
                addnode(s,c,d,1,i,tot);
                addnode(s,a-1,b-1,1,i,tot);
                addnode(s,c,b-1,-1,i,tot);
                addnode(s,a-1,d,-1,i,tot);
            }
        }
        memset(Ans,0,sizeof(Ans));
        cdq(1,tot);
        for(int i=1;i<=tot;++i){
            if(Q[i].s==2){
                printf("%d\n",Ans[Q[i].id]);
                i+=3;
            }
        }
    }
    return 0;
}
View Code

 

6.隐藏题目

这是一道高神出的题目。因为这题目前还未出现过,所以这部分题解不公开。

/*

(------这是一个坑------)

*/

 

CDQ分治与整体二分总结

标签:

原文地址:http://www.cnblogs.com/baobaopangzi88/p/4820004.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!