Problem Description
In the shooting game, the player can choose to stand in the position of [1, X] to shoot, you can shoot all the nearest K targets. The value of K may be different on different shootings. There are N targets to shoot, each target occupy
the position of [Li, Ri] , the distance from the starting line is Di(1<=i<=N). Shooting a target can get the bonus points equal to the distance to the starting line. After each shooting targets still exist. Additional, if the previous bonus points is more
than P, then as a reward for the shooting present bonus points doubled(and then check the next one). Player wants to know the bonus points he got in each shot.
Input
The input consists several test cases.
The first line has two integers, N, M, X, P(1<=N, M ,X<=100000, P<=1000000000), N represents the total number of target shooting, M represents the number of shooting.
The next N lines of three integers L, R, D (1<=L<=R<=X, 1<=D<=10000000), occupy the position of [L, R] and the distance from the starting line is D.
The next M lines, each line contains four integers x, a, b, c (1<=x<=X, 0<=a,b<=N, 1<=c<=10000000), and K = ( a * Pre + b ) % c. Pre is the bonus point of previous shooting , and for the first shooting Pre=1. It denotes standing in the position x and the player
can shoot the nearest K targets.
Output
Output M lines each corresponds to one integer.
Sample Input
4 3 5 8 1 2 6 2 3 3 2 4 7 1 5 2 2 2 1 5 3 1 1 10 4 2 3 7
Sample Output
11 10 18
Author
FZU
Source
2014 Multi-University Training Contest 1
思路:按到start line的距离建树,把线段转换成两个点,逐一插入,插入的时候相同的节点可以共享(如pre的左子树跟x的左子树相同则直接让x的左子树的指针只想pre的左子树即可)。详见代码。
#include <cstdio> #include <algorithm> #define LL long long using namespace std; struct P{ int pos,val,flag; bool operator<(const struct P &p) const { if(pos==p.pos) { if(val==p.val) return flag<p.flag; else return val<p.val; } else return pos<p.pos; } }pt[4000000],ts;//把每条线段转换成两个点,flag=1代表起点,falg=-1代表终点 int ls[4000000],rs[4000000],T[200005],vv[100000],cnum[4000000],nodenum;//ls指向左子树,rs指向右子树,T[i]为树i的根,cnum记录当前节点插入的值的个数 LL sum[4000000]; void insert(int s,int e,int val,int flag,int pre,int &x)//在pre的基础上插入一个点,如果插左边则右子树与pre的右子树相同,直接让x的右子树指向 { //pre的右子树,再新建左子树 x=++nodenum; ls[x]=ls[pre];//先让x的左右子树都指向pre的左右子树 rs[x]=rs[pre]; sum[x]=sum[pre]+flag*vv[val];//求和 cnum[x]=cnum[pre]+flag;//记录该节点插入的值的个数 if(s!=e) { int mid=(s+e)>>1; if(val<=mid) insert(s,mid,val,flag,ls[pre],ls[x]);//插左边就在pre左子树的基础上建x的左子树 else insert(mid+1,e,val,flag,rs[pre],rs[x]);//右边同理 } } LL query(int s,int e,int k,int idx) { if(s==e) return k*vv[s];//如果到了叶子节点返回k*距离 int mid=(s+e)>>1; if(cnum[ls[idx]]>k) return query(s,mid,k,ls[idx]);//如果插入左子树的值的个数大于k就在左子树找 else if(cnum[ls[idx]]==k) return sum[ls[idx]];//如果插入左子树的值的个数等于k就直接返回左子树的sum else return sum[ls[idx]]+query(mid+1,e,k-cnum[ls[idx]],rs[idx]);//如果插入左子树的值的个数小于k就在右子树找(k-左子树插入的个数) } int main() { int n,m,x,p,i,l,r,val,cnt,t; LL pre,pos,a,b,c,k,ans; while(~scanf("%d%d%d%d",&n,&m,&x,&p)) { for(i=0;i<n;i++) { scanf("%d%d%d",&l,&r,&val); vv[i]=val; pt[i*2].pos=l; pt[i*2].val=val; pt[i*2].flag=1; pt[i*2+1].pos=r+1; pt[i*2+1].val=val; pt[i*2+1].flag=-1; } sort(vv,vv+n); cnt=unique(vv,vv+n)-vv;//把距离重复的去掉 int lxd=0;//当前已经建好的树的数量 for(i=0;i<n*2;i++) pt[i].val=lower_bound(vv,vv+cnt,pt[i].val)-vv;//把距离转化成在vv数组里面的下标 sort(pt,pt+n*2);//按(pos,val,flag)升序排序 T[0]=ls[0]=rs[0]=sum[0]=cnum[0]=nodenum=0; pre=1; for(i=0;i<m;i++) { scanf("%I64d%I64d%I64d%I64d",&pos,&a,&b,&c); k=(a*pre+b)%c; ts.pos=pos; ts.val=cnt; t=upper_bound(pt,pt+n*2,ts)-pt;//找到pos对应的第t棵树 while(lxd<t)//如果第t棵树还没建好就建树一直到建好第t棵树为止 { insert(1,cnt,pt[lxd].val,pt[lxd].flag,T[lxd],T[lxd+1]);//在第lxd棵树的基础上建第lxd+1棵树 lxd++; } ans=query(1,cnt,k,T[t]);//在第t棵树上查询 if(pre>p) ans*=2; pre=ans; printf("%I64d\n",ans); } } }
HDU-4866-Shooting(函数式线段树)