[BZOJ2850]巧克力王国(K-D tree)

=== ===

这里放传送门

=== ===

题解

首先一个重要的转化就是如果把给定的(x,y)看做平面上的点,那么因为询问要满足 ax+byc ,那么实际上就是查询一条直线下方的点权和。那么每次判定一棵子树所代表的区域如果完全在直线下方就可以返回答案,否则就递归进去搜索。判断的时候分类讨论比较麻烦。。注意的问题是如果在每次调用函数之间就判断一下是不是要进去就比先无脑进去再判断是否退出要快很多。。ATP这里实测两种写法速度相差10倍+。。。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const long long inf=1e18;
int n,m,D;
long long ans;
struct Node{
    Node *ch[2];
    int Min[2],Max[2],d[2];
    long long sum,val;
    Node();
    Node(int X,int Y,int C);
    void count();
    long long get(long long a,long long b){
        return (long long)d[0]*a+(long long)d[1]*b;
    }
    int check(long long a,long long b,long long c){
        int cnt=0;
        if ((long long)Max[0]*a+(long long)Max[1]*b<c) ++cnt;
        if ((long long)Max[0]*a+(long long)Min[1]*b<c) ++cnt;
        if ((long long)Min[0]*a+(long long)Max[1]*b<c) ++cnt;
        if ((long long)Min[0]*a+(long long)Min[1]*b<c) ++cnt;
        return cnt;
    }
}t[50010],*null,*Root;
Node::Node(){
    ch[1]=ch[0]=null;sum=val=0;
    Max[0]=Min[0]=d[0]=0;
    Max[1]=Min[1]=d[1]=0;
}
Node::Node(int X,int Y,int C){
    ch[1]=ch[0]=null;sum=val=C;
    Max[0]=Min[0]=d[0]=X;
    Max[1]=Min[1]=d[1]=Y;
}
void Node::count(){
    sum=ch[0]->sum+ch[1]->sum+val;
    if (ch[0]!=null)
      for (int i=0;i<2;i++){
          Max[i]=max(Max[i],ch[0]->Max[i]);
          Min[i]=min(Min[i],ch[0]->Min[i]);
      }
    if (ch[1]!=null)
      for (int i=0;i<2;i++){
          Max[i]=max(Max[i],ch[1]->Max[i]);
          Min[i]=min(Min[i],ch[1]->Min[i]);
      }
}
int comp(Node a,Node b){
    return a.d[D]<b.d[D]||(a.d[D]==b.d[D]&&a.d[D^1]<b.d[D^1]);
}
Node *build(int l,int r,int d){
    if (l>r) return null;
    int mid=(l+r)>>1;D=d;
    nth_element(t+l,t+mid,t+r+1,comp);
    t[mid].ch[0]=build(l,mid-1,d^1);
    t[mid].ch[1]=build(mid+1,r,d^1);
    t[mid].count();return t+mid;
}
void Query(Node *now,int a,int b,long long c){
    int dl,dr;
    if (now==null) return;
    if (now->check(a,b,c)==4){
        ans+=now->sum;return;
    }
    if (now->get(a,b)<c) ans+=now->val;
    dl=now->ch[0]->check(a,b,c);
    dr=now->ch[1]->check(a,b,c);
    if (dl==4) ans+=now->ch[0]->sum;
    else if (dl!=0) Query(now->ch[0],a,b,c);
    if (dr==4) ans+=now->ch[1]->sum;
    else if (dr!=0) Query(now->ch[1],a,b,c);
}
int main()
{
    null=new Node;*null=Node();
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++){
        int x,y,c;
        scanf("%d%d%d",&x,&y,&c);
        t[i]=Node(x,y,c);
    }
    Root=build(1,n,0);
    for (int i=1;i<=m;i++){
        int a,b,c;ans=0;
        scanf("%d%d%d",&a,&b,&c);
        Query(Root,a,b,c);
        printf("%I64d\n",ans);
    }
    return 0;
}

偏偏在最后出现的补充说明

K-D tree这玩意儿本来就是个暴力。。写的时候一定要特别注意细节才行。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值