CDQ分治入门---蝗灾、Mokia

题意:W*W(W <= 500000)的矩阵,初始全是0,N(N <= 200000)个询问,单点加一个值,区间查询和。
分析:这题一看数据结构,二维线段树显然是不行的,不过还是可以水30分~

#include
#include
#include
using namespace std;
const int maxn=5500;
int c[maxn][maxn];
int lowbit(int x){
    return x&-x;
}
void add(int x,int y,int k){
    for(int i=x;ifor(int j=y;jint gsum(int x,int y){
    int ans=0;
    for(int i=x;i;i-=lowbit(i)){
        for(int j=y;j;j-=lowbit(j)){
            ans+=c[i][j];   
        }
    }
    return ans;
}

int query(int x1,int y1,int x2,int y2){
    return gsum(x2,y2)+gsum(x1-1,y1-1)-gsum(x2,y1-1)-gsum(x1-1,y2); 
}

int main(){
    freopen("locust.in","r",stdin);
    freopen("locust.out","w",stdout);
    int w,n;
    scanf("%d %d",&w,&n);
    int flag,a,b,c,d;
    while(n--){
        scanf("%d",&flag);
        if(flag==1){
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,c);
        }else{
            scanf("%d%d%d%d",&a,&b,&c,&d);
            printf("%d\n",query(a,b,c,d));
        }
    }
    return 0;
}

当然,我们是无法满足于30分的,所以要写正解CDQ分治…
定义solve(l, r):寻找所有的修改操作∈(l, mid),计算他们对区间(mid+1,r)中查询操作的影响,然后不断分治。
对于这道题,我们在每次分治过程中,将区间内的操作按x轴排序,然后用一个一维的树状数组计算操作状态,然后查询操作直接计算即可。

#include
#include
#include
#include
#include
using namespace std;
const int maxn=200005;
struct work{
    int x1,y1,x2,y2,op,k,num;
    int ans;
}p[maxn],cc[maxn<<1];
int c[500005];
int w,n;

inline int in(){
    int TEMP,EPX;
    TEMP=0;EPX=getchar();
    while(EPX<48||EPX>57)
        EPX=getchar();
    for(;EPX>47&&EPX<58;EPX=getchar())
        TEMP=(TEMP<<3)+(TEMP<<1)+EPX-48;
    return TEMP;
}

//树状数组 ***************
int lowbit(int x){
    return x&(-x);
}

void add(int x,int k){
    for(int i=x;i<=w;i+=lowbit(i)){
        c[i]+=k;
    }
}

int sum(int x){
    int q=0;
    for(int i=x;i;i-=lowbit(i)){
        q+=c[i];    
    }
    return q;
}
//************************

bool cmp(const work&a,const work&b){
    if(a.x1==b.x1) return a.opreturn a.x1void solve(int l,int r){   //CDQ分治 
    if(l==r) return;
    int mid=(l+r)>>1;
    //printf("%d%d\n",l,r);
    int cnt=0;
    for(int i=l;i<=mid;i++){   // (l,mid)区间中修改操作 
        if(p[i].op==1)
            cc[cnt++]=p[i]; 
    }
    for(int i=mid+1;i<=r;i++){  //  (mid+1,r)区间中查询操作 
        if(p[i].op==2){
            cc[cnt++]=p[i];
            cc[cnt++]=p[i];
            cc[cnt-2].x1--;        //查询时需要用 x2的值减x1-1的值是区间(x1,x2) 的值 
            cc[cnt-1].x1=p[i].x2;
            cc[cnt-1].op=3;
        }
    }
    sort(cc,cc+cnt,cmp);  //根据x轴排序 
    for(int i=0;iif(cc[i].op==1){
            add(cc[i].y1,cc[i].k);   //修改 
        }
        else if(cc[i].op==2){
            p[cc[i].num].ans -= sum(cc[i].y2)-sum(cc[i].y1-1);   // x1-1前的修改值 
        }
        else {
            p[cc[i].num].ans += sum(cc[i].y2)-sum(cc[i].y1-1);  // x2前的修改值 
        }
    }
    for(int i=0;iif(cc[i].op==1){
            add(cc[i].y1,-cc[i].k);    //清空c树状数组 
        }
    }
    solve(l,mid);
    solve(mid+1,r);          //分治 
    return ;
}

int MAIN(){
    freopen("locust.in","r",stdin);
    freopen("locust.out","w",stdout);
    scanf("%d %d",&w,&n);
    int flag,a,b,c,d;
    for(int i=1;i<=n;i++){
        //scanf("%d",&flag);
        flag=in();
        if(flag==1){
            //scanf("%d%d%d",&a,&b,&c);
            a=in();b=in();c=in();
            p[i].op=1;
            p[i].x1=a;
            p[i].y1=b;
            p[i].k=c;
        }else{
            //scanf("%d%d%d%d",&a,&b,&c,&d);
            a=in();b=in();c=in();d=in();
            p[i].op=2;
            p[i].x1=min(a,c);
            p[i].y1=min(b,d);
            p[i].x2=max(a,c);
            p[i].y2=max(b,d);
        }
        p[i].num=i;
    }
    solve(1,n);
    for(int i=1;i<=n;i++){
        if(p[i].op==2) printf("%d\n",p[i].ans); 
    }
    return 0;
}
int main(){;}
int helenkeller=MAIN();

第一道CDQ分治就这样解决啦!!!
然后发现COGS上还有一道类似的题,叫 1752. [BOI2007]摩基亚Mokia
然后我以为这两个题是一样的,于是改了下数据范围就交了,然后,我就成功的MLE了…MLE了……..
然后我发现了我的程序的弊端,就是对于每次分治操作的时候,我另开了一个数组cc[],然而其实并不需要…
由于我实在懒得改…所以这里Orz Mikumikumi 学长…

#include
#include
#include
using namespace std;
int cmd,maxn=2000010,n,tot=0,ans[10010]={0},Bit[2000010]={0},H[160010];
class miku
{
    public:
    int x,y,k,s;
    int q;
    miku(){}
    miku(int x1,int y1,int k1,int v1,int t)
    {
        x=x1,y=y1,k=k1,s=v1,q=t;
    }
    bool operator <(const miku a) const
    {
        return x2000010];
int lowbit(int x)
{
    return x&-x;
}
int query(int x)
{
    int re=0;
    while(x>0){re+=Bit[x];x-=lowbit(x);}
    return re;
}
void add(int x,int y)
{
    while(x<=n){Bit[x]+=y;x+=lowbit(x);}
}
void solve(int l,int r)
{
    if(l==r) return;
    int mid=(l+r)>>1;
    solve(l,mid);solve(mid+1,r);
    sort(Q+l,Q+mid+1);sort(Q+mid+1,Q+r+1);
    int L=l,inq=0;
    for(int i=mid+1;i<=r;i++)
    {
        if(Q[i].k==2)
        {
            while(L<=mid&&Q[L].x<=Q[i].x)
            {
                if(Q[L].k==1)
                {
                add(Q[L].y,Q[L].s);
                H[++inq]=L;
                }
            L++;
           }
           ans[Q[i].q]+=Q[i].s*query(Q[i].y);
        }
        //cout<
    }
    for(int i=1;i<=inq;i++)
        add(Q[H[i]].y,-Q[H[i]].s);
}
void push(int x,int y,int k,int v,int t)
{
    if(x>0&&y>0)
    Q[++tot]=miku(x,y,k,v,t);
}
int main()
{
    freopen("mokia.in","r",stdin);
    freopen("mokia.out","w",stdout);
    int x1,y1,x2,y2,v,tail=0;
    while(scanf("%d",&cmd)&&cmd!=3)
    {
        if(cmd==0) 
        {
            scanf("%d",&n);
        }
        if(cmd==1)
        {
            scanf("%d%d%d",&x1,&x2,&v);
            push(x1,x2,1,v,0);
            //printf("1");
        }
        if(cmd==2)
        {
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            tail++;
            push(x2,y2,2,1,tail);
            push(x1-1,y1-1,2,1,tail);
            push(x1-1,y2,2,-1,tail);
            push(x2,y1-1,2,-1,tail);
            //printf("2");
        }
    }
    solve(1,tot);
    for(int i=1;i<=tail;i++) printf("%d\n",ans[i]);
    return 0;
}

然后还没完…我发现[IOI2001]移动电话 貌似和这两个题一样…这TM真的是IOI么这么水…然后我又有新的发现…这题数据范围简直小啊..这就可以直接用用二维树状数组搞了…而且貌似暴力也可以过…
我就不贴代码了…题你们也自己找~

                                             HelenKeller
                                             2016.7.7

你可能感兴趣的:(算法,CDQ分治,树状数组)