HDU 4866 Shooting 题解:主席树

这题的主要的坑点就是他给你的射击目标有重合的部分,如果你向这些重合的部分射击的话要考虑两种情况:

射击目标数量 ≥ 重合数量 : 全加上

射击目标数量 ≤ 重合数量 : 只加距离*射击目标数量

然而这题的内存还是很良心的,总体比较水吧。


主要做法是按照横坐标1~x建立主席树,每棵主席树维护l,r区间的设计目标数量,以及这些数量如果全部被射击获得的分数,这些在建树的时候是很好维护的。

然后对这些线段的处理要用扫描线的思想,就(左端点)建立一个(+1)的入点,(右端点+1)的位置建立一个(-1)的出点,然后从左往右扫一遍即可。


具体过程见代码:


#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#define INF 2*0x3f3f3f3f

using namespace std;

int n, m, x, p;

struct N { int ls, rs, w; long long sum; } tree[6000100];
int roots[200010];
int b[200010], tot, cnt;

int build_tree(int l, int r) {
    int newnode = tot++;
    tree[newnode].w = 0;
    tree[newnode].sum = 0;
    if (l != r) {
        int mid = (l + r) / 2;
        tree[newnode].ls = build_tree(l, mid);
        tree[newnode].rs = build_tree(mid + 1, r);
    }
    return newnode;
}

int updata(int rt, int pos, int val) {
    int newnode = tot++, tmp = newnode;
    long long add = b[pos - 1];
    tree[newnode].sum = tree[rt].sum + add * val;
    tree[newnode].w = tree[rt].w + val;
    int l = 1, r = cnt;
    while (l < r) {
        int mid = (l + r) / 2;
        if (pos <= mid) {
            tree[newnode].ls = tot++;
            tree[newnode].rs = tree[rt].rs;
            newnode = tree[newnode].ls;
            rt = tree[rt].ls;
            r = mid;
        }
        else {
            tree[newnode].ls = tree[rt].ls;
            tree[newnode].rs = tot++;
            newnode = tree[newnode].rs;
            rt = tree[rt].rs;
            l = mid + 1;
        }
        tree[newnode].sum = tree[rt].sum + add * val;
        tree[newnode].w = tree[rt].w + val;
    }
    return tmp;
}

long long query(int rt, int k) {
    //printf("rt = %d k = %d\n",rt,k);
    int l = 1, r = cnt;
    long long ans = 0;
    while (l < r) {
        int mid = (l + r) / 2;
        int tmp = tree[tree[rt].ls].w;
        if (tmp >= k) {
            rt = tree[rt].ls;
            r = mid;
        }
        else {
            k -= tmp;
            ans += tree[tree[rt].ls].sum;
            rt = tree[rt].rs;
            l = mid + 1;
        }
    }
    if (tree[rt].w != 0)
        ans += tree[rt].sum / tree[rt].w * min(k, tree[rt].w);
    return ans;
}

struct P {
    int x, y, val;
    P() {}
    P(int _x, int _y, int _val) : x(_x), y(_y), val(_val) {}
    bool operator < (const P &rhs) const {
        return x < rhs.x;
    }
} pois[500010];

void print(int rt, int l = 1, int r = cnt) {
    printf("l = %d r = %d w = %d sum = %I64d\n", l, r, tree[rt].w, tree[rt].sum);
    if (l != r) {
        int mid = (l + r) / 2;
        print(tree[rt].ls, l, mid);
        print(tree[rt].rs, mid + 1, r);
    }
}

int main() {
    //freopen("in.in", "r", stdin);
    //freopen("out.out", "w", stdout);
    while (scanf("%d %d %d %d", &n, &m, &x, &p) != EOF) {
        int l1, l2, d;
        int ps = 0;
        for (int i = 0; i < n; i++) {
            scanf("%d %d %d", &l1, &l2, &d);
            b[i] = d;
            pois[ps++] = P(l1, d, 1);
            pois[ps++] = P(l2 + 1, d, -1);
        }
        sort(b, b + n);
        cnt = unique(b, b + n) - b;

        sort(pois, pois + ps);

        tot = 0;
        roots[0] = build_tree(1, cnt);
        int t = 0;
        for (int i = 1; i <= x; i++) {
            roots[i] = roots[i - 1];
            while (t < ps && pois[t].x == i) {
                int tmp = (int)(lower_bound(b, b + cnt, pois[t].y) - b) + 1;
                roots[i] = updata(roots[i], tmp, pois[t].val);
                t++;
            }
        }
        /*
        for (int i = 1; i <= x; i++) {
            printf("**********************tree %d is : \n", i);
            print(roots[i]);
        }
        */

        long long now = 1;
        int xs, as, bs, cs;
        for (int i = 0; i < m; i++) {
            //printf("i = %d\n", i);
            scanf("%d %d %d %d", &xs, &as, &bs, &cs);
            long long tmp = query(roots[xs], (now * as + bs) % cs);
            if (now > p) tmp *= 2;
            printf("%I64d\n", tmp);
            now = tmp;
        }
}
return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值