hdu 4866/bzoj 3932 Shooting/任务查询系统(主席树)

本文介绍了一种使用主席树和差分技巧处理线段射击问题的方法。通过维护线段到x轴的距离来计算得分,利用主席树进行快速查询与更新,解决了线段可能重叠的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

     有n条平行于x轴的线段,m次射击,每次射击射中前k个线段,得到的分数为 ki=1di ∑ i = 1 k d i 其中 di d i 为线段到x轴距离,k为 (apre+b)modc ( a ∗ p r e + b ) mod c ,pre为上次得分,并且如果pre大于p,那么本次得分翻倍

思路:

    主席树处理出的是一个前缀和,而利用差分,就能很好的染出一片区域。意思就是,对于每条线段,我们在 l l +d,在 r+1d r + 1 处 − d ,然后我们对于每一个x坐标建立一棵线段树,这样就能产生一颗主席树。我们可以发现,这样的树,可以维护每个x轴坐标上的线段情况,我们维护num和sum两个数组,一个记录此时下面有几个点,一个维护分数和,在每次射击的时候,利用数量向下走即可
    注意,可能出现线段重合的情况,这时候需要加一些判断

    更新:和任务查询系统一模一样。。。

错误及反思:

代码:

#include<bits/stdc++.h>
using namespace std;
#define lson l,m
#define rson m+1,r
#define fi first
#define se second
const int N = 200010;

int ls[N*30],rs[N*30],num[N*30],n,m,x,p,cnt,root[N];
long long sum[N*30];

vector<int> v;
struct LINE{
    int l,r,d;
}line[N];

vector<pair<int,int> > need[N];

int getid(int x){return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}

void init(){
    cnt=0;
    v.clear();
}

int build(int l,int r){
    int rt=++cnt;
    ls[rt]=rt; rs[rt]=rt; sum[rt]=0; num[rt]=0;
    if(l==r) return rt;
    int m=l+r>>1;
    ls[rt]=build(lson);
    rs[rt]=build(rson);
    return rt;
}

int update(int pre,int val,int pos,int l,int r){
    int rt=++cnt;
    sum[rt]=sum[pre]+val; ls[rt]=ls[pre]; rs[rt]=rs[pre]; num[rt]=num[pre]+(val>0?1:-1);
    if(l==r) return rt;
    int m=l+r>>1;
    if(m>=pos) ls[rt]=update(ls[pre],val,pos,lson);
    else rs[rt]=update(rs[pre],val,pos,rson);
    return rt;
}

long long query(int r2,int k,int l,int r){
    if(l==r){
        if(num[r2]==0) return 0;
        if(num[r2]>=k)
            return sum[r2]/num[r2]*k;
        return sum[r2];
    }
    int m=l+r>>1;
    if(num[ls[r2]]>=k) return query(ls[r2],k,lson);
    return sum[ls[r2]]+query(rs[r2],k-num[ls[r2]],rson);
}

int main(){
    while(scanf("%d%d%d%d",&n,&m,&x,&p)!=EOF){
        init();
        for(int i=1;i<=n;i++){
            scanf("%d%d%d",&line[i].l,&line[i].r,&line[i].d);
            v.push_back(line[i].d);
        }
        sort(v.begin(),v.end()),v.erase(unique(v.begin(),v.end()),v.end());

        for(int i=1;i<=n;i++){
            need[line[i].l].push_back({getid(line[i].d),line[i].d});
            need[line[i].r+1].push_back({getid(line[i].d),-line[i].d});
        }

        root[0]=build(1,v.size());
        for(int i=1;i<=x;i++){
            int bef=root[i-1];
            for(int j=0;j<need[i].size();j++){
                bef=update(bef,need[i][j].se,need[i][j].fi,1,v.size());
            }
            root[i]=bef;
        }
        long long pre=1;
        while(m--){
            int xx,a,b,c;
            scanf("%d%d%d%d",&xx,&a,&b,&c);
            long long k=(1ll*a*pre+b)%c;
            k=query(root[xx],(int)k,1,v.size());
            if(pre>p) k*=2;
            pre=k;
            printf("%lld\n",pre);
        }
        for(int i=1;i<=x+1;i++) need[i].clear();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值