=== ===
这里放传送门
=== ===
题解
首先一个重要的转化就是如果把给定的(x,y)看做平面上的点,那么因为询问要满足 ax+by≤c ,那么实际上就是查询一条直线下方的点权和。那么每次判定一棵子树所代表的区域如果完全在直线下方就可以返回答案,否则就递归进去搜索。判断的时候分类讨论比较麻烦。。注意的问题是如果在每次调用函数之间就判断一下是不是要进去就比先无脑进去再判断是否退出要快很多。。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这玩意儿本来就是个暴力。。写的时候一定要特别注意细节才行。。