HDU-4866-Shooting(函数式线段树)

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(函数式线段树)

时间: 2024-12-29 23:44:15

HDU-4866-Shooting(函数式线段树)的相关文章

HDU 4866 Shooting(持久化线段树)

view code//第二道持久化线段树,照着别人的代码慢慢敲,还是有点不理解 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; typedef long long ll; #define lson l,m,ls[rt] #define rson m+1,r,rs[r

HDU 4866 Shooting (主席树)

题目链接  HDU 4866 题意  给定$n$条线段.每条线段平行$x$轴,离x轴的距离为$D$,覆盖的坐标范围为$[L, R]$.   现在有$m$次射击行动,每一次的射击行动可以描述为在横坐标x处找到离x轴最近的$k$条线段,   并计算这$k$个目标距离$x$轴的总和.强制在线. 对线段到$x$轴的距离离散化. 以横坐标为下标建立主席树. 应用差分思想,对于每条线段,在$L$处标记$+1$,在R+1处标记$-1$, 查询的时候在横坐标x处则查找横坐标$x$对应的主席树即可. 主席树维护两

hdu 2665 可持久化线段树求区间第K大值(函数式线段树||主席树)

http://acm.hdu.edu.cn/showproblem.php?pid=2665 Problem Description Give you a sequence and ask you the kth big number of a inteval. Input The first line is the number of the test cases. For each test case, the first line contain two integer n and m (

主席树/函数式线段树/可持久化线段树

什么是主席树 可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本.同时充分利用它们之间的共同数据来减少时间和空间消耗. 因此可持久化线段树也叫函数式线段树又叫主席树. 可持久化数据结构 在算法执行的过程中,会发现在更新一个动态集合时,需要维护其过去的版本.这样的集合称为是可持久的. 实现持久集合的一种方法时每当该集合被修改时,就将其整个的复制下来,但是这种方法会降低执行速度并占用过多的空间. 考虑一个持久集合S. 如图所示,对集合的

HDU 4902 Nice boat(线段树)

HDU Nice boat 题目链接 题意:给定一个序列,两种操作,把一段变成x,把一段每个数字,如果他大于x,就变成他和x的gcd,求变换完后,最后的序列. 思路:线段树,每个结点多一个cover表示该位置以下区间是否数字全相同,然后每次延迟操作,最后输出的时候单点查询即可 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 1

hdu 2795 Billboard(线段树)

Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 10890    Accepted Submission(s): 4827 Problem Description At the entrance to the university, there is a huge rectangular billboard of

hdu 3016 Man Down (线段树 + dp)

题目大意: 是男人就下一般层...没什么可以多说的吧. 注意只能垂直下落. 思路分析: 后面求最大值的过程很容易想到是一个dp的过程 . 因为每一个plane 都只能从左边 从右边下两种状态. 然后我们所需要处理的问题就是 ,你如何能快速知道往左边下到哪里,往右边下到哪里. 这就是线段树的预处理. 讲线段按照高度排序. 然后按照高度从小到大加入到树中. 然后去寻找左端点 和 右端点最近覆盖的线段的编号. #include <cstdio> #include <iostream> #

HDU 3954 Level up 线段树

---NotOnlySuccess 出的题--- 看了题之后觉得和HDU 4027有点像,给的K很小,只有10,目测只要有人升级的时候直接更新到叶子节点就ok了.不过不同于HDU 4027 的是,那题每一次更新都相当于这题的一次升级操作,这题里面可能会出现一次操作之后没有升级和出现升级两种情况,一时半会没了思路. 无奈去搜题解,发现我只要维护一个区间当中距离升级最近的人所需要的基础升级经验,即不算等级加成的裸的升级经验,如果在一次涨经验之后,出现当前区间当中有人会升级,直接将每一个要升级的人更新

多校训练hdu --Nice boat(线段树,都是泪)

Nice boat Time Limit: 30000/15000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 47 Accepted Submission(s): 10 Problem Description There is an old country and the king fell in love with a devil. The devil always ask