2015北京网络赛 J Clarke and puzzle 分块+bitset

转载自here

分块的原因是因为空间复杂度太高,不然的话可以直接开bitset<50000> b[5][50000]

题意:

给5w个5维坐标,5w次询问:给出一个点,求5维都不大于这个点的点数量,强制在线

思路:

首先,如果维数低的话,就直接裸着跑kdtree就好了。
但是这个有五维,所以 kdtree 就不要想了
我写的是分块+ bitset
我预处理 5n bitset bitset[i][j] 表示在第i维,当前排名为 jn 的时候的 bitset 的样子
那么每次询问的时候,我们就可以从最近的 bitset 里面转移过来就好了
对于每一个case,预处理的复杂度是 O(n) 的,询问的复杂度是 5n ,所以跑过去了~

代码:

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <set>
#include <bitset>
#include <vector>
#include <sstream>
#include <queue>
#include <typeinfo>
#include <fstream>
#include <map>
#include <stack>
typedef long long ll;
using namespace std;
//freopen("D.in","r",stdin);
//freopen("D.out","w",stdout);
#define sspeed ios_base::sync_with_stdio(0);cin.tie(0)
#define maxn 1006
#define mod 1000000007
#define eps 1e-9
#define PI acos(-1)
const double EP  = 1E-10 ;
int Num;
//const int inf=0x7fffffff;
const ll inf=999999999;
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//*************************************************************************************

bitset<50001> b[7][270];
bitset<50001> Ans[7];
int now[7];
struct node
{
    int x,y;
};
bool cmp(node a,node b)
{
    return a.x<b.x;
}
node a[7][50001];
int l[300],r[300];
int belong[50001];
int main()
{
    int t;scanf("%d",&t);
    for(int cas=1;cas<=t;cas++)
    {
        int n=read(),m=read();
        for(int i=1;i<=5;i++)
            for(int j=1;j<250;j++)
                b[i][j].reset();
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=5;j++)
            {
                a[j][i].x=read();
                a[j][i].y=i;
            }
        }
        for(int i=1;i<=5;i++)
            sort(a[i]+1,a[i]+n+1,cmp);
        int block = sqrt(n);
        int num = n/block;
        if(n%block)num++;
        for(int i=1;i<=num;i++)
            l[i]=(i-1)*block+1,r[i]=i*block;
        r[num]=n;
        for(int i=1;i<=n;i++)
            belong[i]=(i-1)/block+1;
        for(int i=1;i<=5;i++)
        {
            for(int j=1;j<=num;j++)
            {
                b[i][j]|=b[i][j-1];
                for(int k=l[j];k<=r[j];k++)
                    b[i][j][a[i][k].y]=1;
            }
        }

        int q=read();
        int lastans = 0;
        while(q--)
        {
            for(int i=1;i<=5;i++)
                now[i]=read();
            for(int i=1;i<=5;i++)
                now[i]^=lastans;
            for(int i=1;i<=5;i++)
                Ans[i].reset();
            for(int i=1;i<=5;i++)
            {
                int L = 0,R = n;
                while(L<=R)
                {
                    int mid = (L+R)/2;
                    if(now[i]>=a[i][mid].x)
                        L = mid+1;
                    else
                        R = mid-1;
                }
                int p = L-1;
                if(p==0)continue;
                Ans[i]|=b[i][belong[p]-1];
                for(int j=l[belong[p]];j<=p;j++)
                    Ans[i][a[i][j].y]=1;
            }
            Ans[1] = Ans[1]&Ans[2]&Ans[3]&Ans[4]&Ans[5];
            lastans = Ans[1].count();
            printf("%d\n",lastans);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值