XJOI NOIP2015模拟赛Day1 T2 ctps bitset优化 或 排序+cdq分治+树状数组+平衡树

题意:

4维空间中有1个点集A,|A|=n,用(a,b,c,d)表示每个点。

共有m个询问,每次询问输入一个点(a,b,c,d),求最大的S,其中S={p|p∈A且ap<=a,bp<=b,cp<=c,dp<=d},输出|S|

输入格式:

第一行n

接下来n行有n个4维点对

第n+2行有一个数m

再接下来m行每行有一个四维点对,表示每个询问

输出格式:

对于每个询问输出一个数

**方法:**bitset优化 或 排序+cdq分治+树状数组+平衡树

解析:

神题,考场不会,暴力骗40,据说bz陌上花开跟这道题类似?差一维呢!

基本思想是降维

不过怎么做呢?考场我也就想了个排序降1维之后弃疗

后来才懂怎么降,太神了

首先第一维排序,然后对第二维cdq分治。

第一维不用说,精度也不卡,随便排一下就好了,之后cdq分治步骤:划分->递归左区间->右区间询问处理->搞右区间->归并

妈妈我之前学个鬼cdq了

之后有个需要注意的,就是排序第一维的时候,第二关键字是是否是询问。

cdq前sort下第二维,或者你在里面搞也行无所谓了。不过外面搞快,这是必然的。

然后呢,上一个树状数组。

每个树状数组节点再开个平衡树。

好像是我刚写了个线段树套线段树所以这部分好写咯?

注意:第三维需要离散,不然开不了树状数组。

每次搞完一次cdq的时候,需要d掉原来的树套树,由于动态开点?所以我们只需要size赋零,清一遍树状数组的root。

当然实测memset root 是可以过的,不过这么做太暴力了,我们可以把树状数组的过程重新演算一遍,把赋过的root搞成0就好了。

之后这道题就OK了?不好写是不好写!

不过蒟蒻我还是艰难地写出来了,最后那个AC真的是真心的爽!


不过呢?

让我们来想一个问题,如果题中的4维变成了5维怎么写?

卧槽树套树套树?

我选择死亡这里写图片描述

所以专门有神奇的优化方式来处理这种问题?

先想一个暴力

对于每一维,单独排序,对于每个询问,肯定都是每一维开始的连续一段能单独满足这一维,所以呢,我们要是找这四维中最短的一段,枚举所有段内的元素,将所有可行的元素添加到答案里。

这很暴力,也很赌数据这里写图片描述

不过有bitset啊

每一次排序所有元素,排序所有询问,之后像cdq中那么处理,添加一段更新一次添加一段更新一次。

讨论第一维的时候直接赋上该询问的bitset,后三维的话就是&原来的bitset就OK了。好像比正解慢了0.2s。

代码:

bitset

#include <bitset>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 30010
using namespace std;
int n,m;
struct node
{
    double a[5];
    int no;
}a[N],b[N];
bitset<N>bit[N],bit2;
int cmp1(node x,node y){if(x.a[1]==y.a[1])return x.no<y.no;return x.a[1]<y.a[1];}
int cmp2(node x,node y){if(x.a[2]==y.a[2])return x.no<y.no;return x.a[2]<y.a[2];}
int cmp3(node x,node y){if(x.a[3]==y.a[3])return x.no<y.no;return x.a[3]<y.a[3];}
int cmp4(node x,node y){if(x.a[4]==y.a[4])return x.no<y.no;return x.a[4]<y.a[4];} 
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%lf%lf%lf%lf",&a[i].a[1],&a[i].a[2],&a[i].a[3],&a[i].a[4]),a[i].no=i;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)scanf("%lf%lf%lf%lf",&b[i].a[1],&b[i].a[2],&b[i].a[3],&b[i].a[4]),b[i].no=i;
    for(int i=1;i<=4;i++)
    {
        switch(i)
        {
            case 1:sort(a+1,a+n+1,cmp1);sort(b+1,b+m+1,cmp1);break;
            case 2:sort(a+1,a+n+1,cmp2);sort(b+1,b+m+1,cmp2);break;
            case 3:sort(a+1,a+n+1,cmp3);sort(b+1,b+m+1,cmp3);break;
            case 4:sort(a+1,a+n+1,cmp4);sort(b+1,b+m+1,cmp4);break;
        }
        int l1=1,l2=1;
        while(l2<=m)
        {
            while(a[l1].a[i]<=b[l2].a[i]&&l1<=n)
            {
                bit2[a[l1].no]=1;
                l1++;
            }
            if(i==1)
                bit[b[l2].no]=bit2;
            else bit[b[l2].no]&=bit2;
            l2++;
        }
        bit2=0;
    }
    for(int i=1;i<=m;i++)printf("%d\n",bit[i].count());
}

正解!!

#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 30010
using namespace std;
int n,m,size,tot;
int lowbit(int x){return x&(-x);}
struct treap
{
    double v;
    int w;
    int l,r,size,rnd;
}tr[N*30];
struct node
{
    double a,b,c,d;
    int newc;
    int no;
    int opt;
}a[N<<1],b[N<<1],nq[N<<1];
int ans[N];
int root[N<<1];
double gainc[N<<1];
int cmp(node x,node y)
{
    if(x.a==y.a)return x.opt<y.opt;
    return x.a<y.a;
}
int cmp2(node x,node y)
{
    if(x.b==y.b)return x.opt<y.opt;
    return x.b<y.b;
}
void pushup(int rt)
{
    tr[rt].size=tr[tr[rt].l].size+tr[tr[rt].r].size+tr[rt].w;
}
void lturn(int &rt)
{
    int t=tr[rt].r;
    tr[rt].r=tr[t].l;
    tr[t].l=rt;
    tr[t].size=tr[rt].size;
    pushup(rt);
    rt=t;
}
void rturn(int &rt)
{
    int t=tr[rt].l;
    tr[rt].l=tr[t].r;
    tr[t].r=rt;
    tr[t].size=tr[rt].size;
    pushup(rt);
    rt=t;
}
void ins(int &rt,double v)
{
    if(!rt)
    {
        rt=++size;
        tr[rt].size=1,tr[rt].l=0,tr[rt].r=0;
        tr[rt].w=1,tr[rt].v=v,tr[rt].rnd=rand();
        return;
    }
    tr[rt].size++;
    if(v==tr[rt].v)tr[rt].w++;
    else if(v<tr[rt].v)
    {
        ins(tr[rt].l,v);
        if(tr[tr[rt].l].rnd<tr[rt].rnd)rturn(rt);
    }else
    {
        ins(tr[rt].r,v);
        if(tr[tr[rt].r].rnd<tr[rt].rnd)lturn(rt);
    }
}
int getrnk(int rt,double v)
{
    if(!rt)return 0;
    if(v<tr[rt].v)
    {
        return getrnk(tr[rt].l,v);
    }else
    {
        int tmp=tr[tr[rt].l].size+tr[rt].w;
        return tmp+getrnk(tr[rt].r,v);
    }
}
void clr(int x)
{
    while(x<=tot&&root[x])
    {
        root[x]=0;
        x+=lowbit(x);
    }
}
void update(int x,double num)
{
    while(x<=tot)
    {
        ins(root[x],num);
        x+=lowbit(x);
    }
}
int getans(int x,double num)
{
    int ret=0;
    while(x)
    {
        ret+=getrnk(root[x],num);
        x-=lowbit(x);
    }
    return ret;
}
void cdq(int l,int r)
{
    int mid=(l+r)>>1;
    if(l>=r)
    {
        return;
    }

    int l1=l,l2=mid+1;
    for(int i=l;i<=r;i++)
    {
        if(a[i].no<=mid)nq[l1++]=a[i];
        else nq[l2++]=a[i];
    }
    for(int i=l;i<=r;i++)a[i]=nq[i];
    cdq(l,mid);
    l1=l;
    for(int i=mid+1;i<=r;i++)
    {
        if(a[i].opt==0)continue;
        while(l1<=mid&&a[l1].b<=a[i].b)
        {
            if(a[l1].opt==0)
            {
                update(a[l1].newc,a[l1].d);
            }
            l1++;
        }
        ans[a[i].opt]+=getans(a[i].newc,a[i].d);    
    }
    size=0;
    while(l1>=l)
    {
        if(a[l1].opt==0)
        {
            clr(a[l1].newc);
        }
        l1--;
    }
    cdq(mid+1,r);
    l1=l,l2=mid+1;
    for(int i=l;i<=r;i++)
    {
        if((a[l1].b<a[l2].b||(a[l1].b==a[l2].b&&a[l1].no<a[l2].no)||l2>r)&&l1<=mid)nq[i]=a[l1++];
        else nq[i]=a[l2++];
    }
    for(int i=l;i<=r;i++)a[i]=nq[i];
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%lf%lf%lf%lf",&a[i].a,&a[i].b,&a[i].c,&a[i].d);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)scanf("%lf%lf%lf%lf",&a[i+n].a,&a[i+n].b,&a[i+n].c,&a[i+n].d),a[i+n].opt=i;
    for(int i=1;i<=n+m;i++)
    {
        gainc[i]=a[i].c;
    }
    sort(gainc+1,gainc+n+m+1);
    tot=unique(gainc+1,gainc+n+m+1)-gainc-1;
    for(int i=1;i<=n+m;i++)
        a[i].newc=lower_bound(gainc+1,gainc+tot+1,a[i].c)-gainc;
    sort(a+1,a+n+m+1,cmp);
    for(int i=1;i<=n+m;i++)a[i].no=i;
    sort(a+1,a+n+m+1,cmp2);
    cdq(1,n+m);
    for(int i=1;i<=m;i++)
    {
        printf("%d\n",ans[i]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值