[POI2011]MET-Meteors 题解

[POI2011]MET-Meteors 题解

题目地址:洛谷:【P3527】[POI2011]MET-Meteors – 洛谷、BZOJ:Problem 2527. — [Poi2011]Meteors

题目描述

Byteotian Interstellar Union (BIU) has recently discovered a new planet in a nearby galaxy. The planet is unsuitable for colonisation due to strange meteor showers, which on the other hand make it an exceptionally interesting object of study.
The member states of BIU have already placed space stations close to the planet’s orbit. The stations’ goal is to take samples of the rocks flying by.
The BIU Commission has partitioned the orbit into mmm sectors, numbered from 111 to mmm , where the sectors 111 and mmm are adjacent. In each sector there is a single space station, belonging to one of the nnn member states.
Each state has declared a number of meteor samples it intends to gather before the mission ends. Your task is to determine, for each state, when it can stop taking samples, based on the meter shower predictions for the years to come.
Byteotian Interstellar Union有N个成员国。现在它发现了一颗新的星球,这颗星球的轨道被分为M份(第M份和第1份相邻),第i份上有第Ai个国家的太空站。 这个星球经常会下陨石雨。BIU已经预测了接下来K场陨石雨的情况。 BIU的第i个成员国希望能够收集Pi单位的陨石样本。你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石。

题意简述

给定一个环,每个节点有一个所属国家,k次事件,每次对[l,r]区间上的每个点点权加上一个值,求每个国家最早多少次操作之后所有点的点权和能达到一个值

输入输出格式

输入格式:
第一行是两个数N,M。 第二行有M个数,第i个数Oi表示第i段轨道上有第Oi个国家的太空站。 第三行有N个数,第i个数Pi表示第i个国家希望收集的陨石数量。 第四行有一个数K,表示BIU预测了接下来的K场陨石雨。 接下来K行,每行有三个数Li,Ri,Ai,表示第K场陨石雨的发生地点在从Li顺时针到Ri的区间中(如果Li<=Ri,就是Li,Li+1,…,Ri,否则就是Ri,Ri+1,…,m-1,m,1,…,Li),向区间中的每个太空站提供Ai单位的陨石样本。

输出格式:
N行。第i行的数Wi表示第i个国家在第Wi波陨石雨之后能够收集到足够的陨石样本。如果到第K波结束后仍然收集不到,输出NIE。

输入输出样例

输入样例#1:

3 5
1 3 2 1 3
10 5 7
3
4 2 4
1 3 1
3 5 2

输出样例#1:

3
NIE
1

说明

数据范围: 1<=n,m,k<=3*10^5 1<=Pi<=10^9 1<=Ai<10^9

题解

参考资料:「BZOJ2527」[POI2011] Meteors – 二分法 – 树状数组 – hzwer.com
我们可以考虑用一个BIT来维护前缀和,二分每个国家的答案。这个算法的复杂度是O(n^2 \log^2 n),不能满足题目的数据范围。
这里就要用一种叫整体二分的写法,思路是考虑对每个国家二分太慢了,我们不如对这些国家一起二分。具体而言,就是我们可以做一个1~k的二分,在二分区间[l, r]的时候将mid可以满足条件的国家扔进左边一个集合中,表示这些国家的答案在[l, mid]中,不满足条件的国家扔进右边一个集合中,表示这些国家的答案在[mid+1, r]中。直到达到终点,即区间左右端点相等,这时便不必划分左右集合了,将大集合中的所有国家标注上答案即可。至于维护前缀和,只需要记下当前树状数组是下了几场陨石雨的,暴力加入/回滚操作即可。当然这个也可以用可持久线段树来写吧,不过那个东西似乎常数比较大。由于不需要枚举每个国家二分了,复杂度降为O(n \log^2 n),可以接受了。
需要注意的是,这个题好像有点卡常。以及会爆int,建议使用long long以及加到满足p就break,避免爆炸。
写的时候经常把二分区间端点和划分集合的数组端点搞混,调了好久233。

代码

// Code by KSkun, 2018/5
#include <cstdio>
#include <cctype>

#include <algorithm>
#include <vector>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    register char c = fgc();
    while(!isdigit(c)) {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(isdigit(c)) {
        res = (res << 1) + (res << 3) + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 300005;

int n, m, k, p[MAXN];
std::vector<int> slist[MAXN];

struct Operation {
    int l, r, a;
} ops[MAXN];

LL bit[MAXN];

inline int lowbit(int x) {
    return x & -x;
}

inline void add(int x, LL v) {
    for(int i = x; i <= m; i += lowbit(i)) {
        bit[i] += v;
    }
}

inline LL query(int x) {
    LL res = 0;
    for(int i = x; i; i -= lowbit(i)) {
        res += bit[i];
    }
    return res;
}

inline void doop(int op, int neg) {
    add(ops[op].l, neg * ops[op].a);
    add(ops[op].r + 1, neg * -ops[op].a);
    if(ops[op].l > ops[op].r) add(1, neg * ops[op].a);
}

int que[MAXN], tmp[MAXN], now = 0, ans[MAXN];
bool can[MAXN];

inline void divide(int l, int r, int lpos, int rpos) {
    if(lpos > rpos) return;
    if(l == r) {
        for(int i = lpos; i <= rpos; i++) {
            ans[que[i]] = l;
        }
        return;
    }
    int mid = (l + r) >> 1;
    while(now < mid) {
        now++; doop(now, 1);
    }
    while(now > mid) {
        doop(now, -1); now--;
    }
    int lcnt = 0;
    for(int i = lpos; i <= rpos; i++) {
        LL tot = 0;
        for(int j = 0; j < slist[que[i]].size(); j++) {
            tot += query(slist[que[i]][j]);
            if(tot >= p[que[i]]) break;
        }
        if(tot >= p[que[i]]) {
            can[que[i]] = true; lcnt++;
        } else {
            can[que[i]] = false; 
        }
    }
    int lpos1 = lpos, lpos2 = lpos + lcnt;
    for(int i = lpos; i <= rpos; i++) {
        if(can[que[i]]) tmp[lpos1++] = que[i];
        else tmp[lpos2++] = que[i];
    }
    for(int i = lpos; i <= rpos; i++) {
        que[i] = tmp[i];
    }
    divide(l, mid, lpos, lpos1 - 1);
    divide(mid + 1, r, lpos1, lpos2 - 1);
}

int main() {
    n = readint(); m = readint();
    for(int i = 1, oi; i <= m; i++) {
        oi = readint(); slist[oi].push_back(i);
    }
    for(int i = 1; i <= n; i++) {
        p[i] = readint();
    }
    k = readint();
    for(int i = 1; i <= k; i++) {
        ops[i].l = readint();
        ops[i].r = readint();
        ops[i].a = readint();
    }
    ops[++k] = Operation {1, m, int(1e9)};
    for(int i = 1; i <= n; i++) {
        que[i] = i;
    }
    divide(1, k, 1, n);
    for(int i = 1; i <= n; i++) {
        if(ans[i] < k) printf("%d\n", ans[i]);
        else puts("NIE");
    }
    return 0;
}


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据