cogs 1752 [BOI2007]摩基亚Mokia(cdq分治+树状数组)

106 篇文章 0 订阅

数据范围过大,所以没有办法二维树状数组和二维线段树什么的。

听说kdtree可做?然而还不会。

这时候cdq分治就派上用场了,首先它不需要二维的空间,所有空间上是没问题的,然后时间复杂度也可以,具体时间复杂度分析下面再说。

这个问题其实可以看成一个三维偏序问题,(时间,横坐标,纵坐标)。

三维貌似看起来有些头疼,假如是二维呢,比如 问题是动态加点,询问一条线段里包含的权值和,

那样就是(时间,坐标)的一个二维偏序,对于我一个查询可以看成(a,b),(a,c)的两个偏序,a代表查询的时间,b,c代表线段的两个端点,那么我们如果找到时间小于a,坐标小于c的的权值和,再减去同样的比较方法小于(a,b)的权值和,不就求出了这一时刻这条线段里包含的权值和了嘛。

那么三维不就是把坐标扩大,按照求二维前缀和的方式去求容斥一下就好了?

我们需要先将一次询问转换为四次不同的二维前缀和查询操作,然后对于每一个询问,我们需要求出左区间(时间小于自己)里,横坐标小于自己纵坐标也小于自己的权值和,

时间已经满足,横坐标的话可以通过cdq分治时顺带的归并排序确定出横坐标小于自己的连续操作区间,但是纵坐标就需要用树状数组统计一下相应的权值了,在查询的时候再求出纵坐标小于自己的点的权值,每次分治的结束的时候都清空下树状数组,就做完了。

cdq分治递归logn层,每层o(n)的操作,然后加上一个树状数组的logn,时间复杂度就是nlog^nlog^2了。

cogs oj给出数据什么的真的好评,但是我数组开小了,应该re竟然返回wa了。

代码:

#include <stdio.h>

//using namespace std;
const int maxn=2e6+5;
int ans[10005];
struct node
{
    int t;
    int x;
    int y;
    int qid;
    int val;
    int type;
    bool operator <(const node &  a)const 
    {
        return a.x==x?type<a.type:x<a.x;
    }
}que[maxn], tmp[maxn];
int n;

int val[maxn];
int lowbit(int x)
{
    return x&-x;
}
void add(int x, int y)
{
    while(x<maxn)
    {
        val[x]+=y;
        x+=lowbit(x);
    }
    return; 
}

int sum(int x)
{
    int res=0;
    while(x>0)
    {
        res+=val[x];

        x-=lowbit(x);
    }
    return res;
}


void cdq(int l, int r)
{
    if(r-l<=1)return;
    int mid=(l+r)>>1;
    cdq(l, mid);
    cdq(mid, r);
    int p=l, q=mid, o=0;
//    printf("%d %d\n", l, r);
    while(p<mid && q<r)
    {
       if(que[p]<que[q])
       {
          if(que[p].type==1)
          {
             add(que[p].y, que[p].val);
          }
          tmp[o++]=que[p++];
       } 
       else 
       {
          if(que[q].type==2)
          {
            ans[que[q].qid]+=que[q].val*sum(que[q].y);
    //        printf("%d %d %d %d %d\n", que[q].x, que[q].y, que[q].val, que[q].qid, ans[que[q].qid]);
          }
          tmp[o++]=que[q++];
       }
    }
    while(q<r)
    {
          if(que[q].type==2)
          {
            ans[que[q].qid]+=que[q].val*sum(que[q].y);
    //        printf("%d %d %d %d %d\n", que[q].x, que[q].y, que[q].val, que[q].qid, ans[que[q].qid]);
          }

        tmp[o++]=que[q++];
    }
    /*
    memset(val, 0, sizeof val);
    */
    for(int i=l; i<p; i++)if(que[i].type==1)add(que[i].y, que[i].val*-1);


    while(p<mid)
    {
        tmp[o++]=que[p++];
    }
    for(int i=0; i<o; i++)que[i+l]=tmp[i];
 

    return;
}






int main()
{
    /*
    freopen("mokia.in", "r", stdin);
    freopen("mokia.out", "w", stdout);
    */
    
    scanf("%*d%d", &n);
    int x, y, x1, y1, e, id=1, qid=1;
    while(~scanf("%d", &e))
    {
        if(e==3)break;
        if(e==1)
        {
            scanf("%d%d%d", &que[id].x, &que[id].y, &que[id].val);
            que[id].t=id;
            que[id].type=1;
        }   
        else 
        {
            scanf("%d%d%d%d", &x, &y, &x1, &y1);
            que[id].t=id, que[id].x=x-1, que[id].y=y-1, que[id].val=1, que[id].qid=qid, que[id].type=2; 
            id++;
            que[id].t=id, que[id].x=x-1, que[id].y=y1, que[id].val=-1, que[id].qid=qid, que[id].type=2; 
            id++;
            que[id].t=id, que[id].x=x1, que[id].y=y-1, que[id].val=-1, que[id].qid=qid, que[id].type=2; 
            id++;
            que[id].t=id, que[id].x=x1, que[id].y=y1, que[id].val=1, que[id].qid=qid, que[id].type=2; 
            qid++;

        }
        id++;
    }
    cdq(1, id);
    for(int i=1; i<qid; i++)
    {
        printf("%d\n", ans[i]);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值