[BZOJ 2527] [Luogu P3527] [POI2011]MET-Meteors

洛谷传送门
BZOJ传送门

题目描述

Byteotian Interstellar Union有 N N N个成员国。现在它发现了一颗新的星球,这颗星球的轨道被分为 M M M份(第 M M M份和第 1 1 1份相邻),第 i i i份上有第 A i A_i Ai个国家的太空站。 这个星球经常会下陨石雨。BIU已经预测了接下来 K K K场陨石雨的情况。 BIU的第 i i i个成员国希望能够收集 P i P_i Pi单位的陨石样本。你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石。

输入输出格式

输入格式:

第一行是两个数 N , M N,M N,M。 第二行有 M M M个数,第 i i i个数 O i O_i Oi表示第 i i i段轨道上有第 O i O_i Oi个国家的太空站。 第三行有 N N N个数,第 i i i个数 P i P_i Pi表示第 i i i个国家希望收集的陨石数量。 第四行有一个数 K K K,表示BIU预测了接下来的 K K K场陨石雨。 接下来 K K K行,每行有三个数 L i , R i , A i L_i,R_i,A_i Li,Ri,Ai,表示第 K K K场陨石雨的发生地点在从 L i L_i Li顺时针到 R i R_i Ri的区间中(如果 L i ≤ R i Li\leq Ri LiRi,就是 L i L_i Li, L i + 1 L_i+1 Li+1,…, R i R_i Ri,否则就是 R i , R i + 1 , … , m − 1 , m , 1 , … , L i Ri,Ri+1,…,m-1,m,1,…,Li Ri,Ri+1,,m1,m,1,,Li),向区间中的每个太空站提供 A i A_i Ai单位的陨石样本。

输出格式:

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

提示

数据范围: 1 ≤ n , m , k ≤ 3 ∗ 1 0 5 1\leq n,m,k\leq 3*10^5 1n,m,k3105 1 ≤ P i ≤ 1 0 9 1\leq P_i\leq 10^9 1Pi109 1 ≤ A i &lt; 1 0 9 1\leq A_i&lt;10^9 1Ai<109

感谢@noco 提供的翻译

解题分析

蒟蒻A的第一道整体二分题QAQ…
如果有只有一个国家怎么处理?我们可以二分答案再分别计算, 区间加用树状数组维护差分即可, 复杂度 O ( N l o g 2 ( N ) ) O(Nlog^2(N)) O(Nlog2(N))
然后我们发现似乎维护所有国家也差不多,就只需要二分陨石雨次数,枚举所有国家计算, 如果未达到要求就向上二分, 达到了就向下二分, 多的过程只有枚举所有国家计算一遍,总复杂度依然是 O ( N l o g 2 ( N ) ) O(Nlog^2(N)) O(Nlog2(N))
不过需要注意一个国家得到的总陨石数可能爆 l o n g   l o n g long\ long long long, 注意达到了就 b r e a k break break掉。

代码如下:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cctype>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <limits.h>
#define R register
#define W while
#define gc getchar()
#define IN inline
#define MX 300050
#define ll long long
#define lbt(i) (i & -i) 
IN char nc()
{
    static const int buflen=1e6;
    static char buf[buflen],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,buflen,stdin),p1==p2)?EOF:*p1++;
}
template <class T>
IN void in(T &x)
{
	x = 0; R char c = nc();
	W (!isdigit(c)) c = nc();
	W (isdigit(c))
	x = (x << 1) + (x << 3) + c - 48, c = nc();
}
int cont, stat, num, deal, lcnt, rcnt;
int req[MX], bel[MX], que[MX], ans[MX], lbuf[MX], rbuf[MX];
ll tree[MX], cnt[MX];
std::vector <int> dom[MX];
struct Event
{
	int tim, lef, rig, val;
}eve[MX << 1];
namespace BIT
{
	IN void add(R int now, const int &del)
	{W (now <= stat) tree[now]+= del, now += lbt(now);}
	IN void modify(R int lef, R int rig, const int &del)
	{
		if(lef <= rig) add(lef, del), add(rig + 1, -del);
		else add(1, del), add(rig + 1, -del), add(lef, del);
	}
	IN ll query(R int now)
	{
		ll ret = 0;
		W (now) ret += tree[now], now -= lbt(now);
		return ret;
	}
}
namespace CDQ
{
	void solve(const int &tl, const int &tr, const int &cl, const int &cr)
	{
		if((tl > tr) || (cl > cr)) return;
		if(tl == tr)
		{
			for (R int i = cl; i <= cr; ++i) ans[que[i]] = tl;
			return;
		}
		int mid = tl + tr >> 1;
		W (deal < mid) ++deal, BIT::modify(eve[deal].lef, eve[deal].rig, eve[deal].val);
		W (deal > mid) BIT::modify(eve[deal].lef, eve[deal].rig, -eve[deal].val), --deal;//像莫队一样操作消除/增加影响
		for (R int i = cl; i <= cr; ++i)
		{
			cnt[que[i]] = 0;
			for (R int j = dom[que[i]].size() - 1; ~j; --j)
			{cnt[que[i]] += BIT::query(dom[que[i]][j]); if(req[que[i]] <= cnt[que[i]]) break;}//注意break掉
		}
		lcnt = rcnt = 0;
		for (R int i = cl; i <= cr; ++i)
		{
			if(req[que[i]] > cnt[que[i]]) rbuf[++rcnt] = que[i];
			else lbuf[++lcnt] = que[i];//达到的甩左边二分边界,没达到的甩右边
		}
		int cmid = cl + lcnt - 1;
		for (R int i = 1; i <= lcnt; ++i) que[i + cl - 1] = lbuf[i];
		for (R int i = 1; i <= rcnt; ++i) que[i + cmid] = rbuf[i];
		solve(tl, mid, cl, cmid);
		solve(mid + 1, tr, cmid + 1, cr);
	}
}
int main(void)
{
	in(cont), in(stat);
	for (R int i = 1; i <= stat; ++i)
	in(bel[i]), dom[bel[i]].push_back(i);
	for (R int i = 1; i <= cont; ++i) in(req[i]), que[i] = i;
	in(num); for (R int i = 1; i <= num; ++i) in(eve[i].lef), in(eve[i].rig), in(eve[i].val);
	eve[num + 1].lef = 1, eve[num + 1].lef = stat, eve[num + 1].val = INT_MAX;//增加一个点, 所有不能达到指定数目的国家答案都为num+1
	deal = 0;
	CDQ::solve(1, num + 1, 1, cont);
	for (R int i = 1; i <= cont; ++i)
	{
		if(ans[i] > num) printf("NIE\n");
		else printf("%d\n", ans[i]);
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值