CodeForces 589G Hiring

G. Hiring
time limit per test
4 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output

The head of human resources department decided to hire a new employee. He created a test exercise for candidates which should be accomplished in at mostm working days. Each candidate has to pass this test exercise. During thej-th day a candidate is allowed to be in the office for at mosttj units of time.

Overall, n candidates decided to apply for the job and sent out their resumes. Based on data received the head has defined two parameters describing every candidate:di andri. The parameterdi is the time to get prepared for work which thei-th candidate spends each morning. This time doesn't depend on day. The parameterri is the total working time needed for thei-th candidate to accomplish the whole test exercise.

Thus the time spent in the office in the j-th day consists of di units of time to get prepared and some units of time to proceed with the exercise. A candidate can skip entire working day and do not come to the office. Obviously in this case he doesn't spend di units of time to prepare.

To complete the exercise a candidate should spend exactlyri units of time working on the exercise (time to prepare is not counted here).

Find out for each candidate what is the earliest possible day when he can fully accomplish the test exercise. It is allowed to skip working days, but if candidate works during a day then he must spenddi units of time to prepare for work before he starts progressing on the exercise.

Input

The first line contains two integer numbers n,  m (1 ≤ n,  m ≤ 2·105) — the number of candidates and the maximum number of working days to do the test exercise.

The second line contains m integer numberst1, t2, ..., tm(1 ≤ tj ≤ 106) — the durations of working days in time units.

The following n lines contain two integers each:di,  ri(0 ≤ di ≤ 106,  1 ≤ ri ≤ 106) — how much time in the beginning of a day is required for i-th candidate before he starts his work on the test exercise and how much time it is needed for him to accomplish this task.

Output

Output a sequence of n integer numbersb1, b2, ..., bn, wherebi is the earliest day when thei-th candidate can finish the test exercise.

In case the i-th candidate cannot finish the test exercise inm days output bi = 0.

Days in this problem are numbered from 1 to m in the order they are given in the input.

Sample test(s)
Input
3 3
4 2 5
1 3
2 5
3 4
Output
1 3 0 

题目大意如下:

现有N个人和M天,每天自习室分别开放不同的时间。每个人有两个属性,第一个是每天开始学习前颓废的时间,第二个是总共需要学习的时间,对于每个人询问最早能在第几天完成学习。人之间不会相互影响。

因为每个人学习之前都要摆一会儿,所以若某天自习室总开放时间 <= 打摆时间,那么这天可以忽略不计。

那么第一反应就是主席树,查找从第一天到某一天大于某个值的数的个数以及总和,再二分逼近答案。

考虑到开放时间小的某天,我们实际上把它“删除了”,且在某种顺序下每个点只会被删除一次,即按照打摆时间从小到大排序后第一次被某人完全浪费时。

那么将询问按打摆时间从小到大排序,并删除无意义(即完全被浪费)的点,那么此时便可以直接利用线段树二分。


代码如下:

/* 
* @Author: duyixian
* @Date:   2015-10-17 17:03:01
* @Last Modified by:   duyixian
* @Last Modified time: 2015-10-17 19:46:22
*/

#include "cstdio"
#include "cstdlib"
#include "iostream"
#include "algorithm"
#include "cstring"
#include "queue"

using namespace std;

#define MAX_SIZE 200005
#define INF 0x3F3F3F3F
#define Eps
#define Mod

inline int Get_Int()
{
	int Num = 0, Flag = 1;
	char ch;
	do
	{
		ch = getchar();
		if(ch == '-')
			Flag *= -1;
	}
	while(ch < '0' || ch > '9');
	do
	{
		Num = Num * 10 + ch - '0';
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9');
	return Num * Flag;
}

struct Node
{
	long long Left, Right, Mid, Sum, Size, Add_Mark;
};

struct Man
{
	long long a, b, Pos;

	inline bool operator < (Man const &c) const
	{
		return a < c.a;
	}
}A[MAX_SIZE];

struct Day
{
	long long Time, Pos;

	inline bool operator < (Day const &a) const
	{
		return Time < a.Time;
	}
}D[MAX_SIZE];

int N, M;
long long Time[MAX_SIZE], Ans[MAX_SIZE];

struct Segment_Tree
{
	Node Nodes[MAX_SIZE * 8];

	inline void Push_Down(int Now)
	{
		Nodes[Now].Sum += Nodes[Now].Size * Nodes[Now].Add_Mark;
		Nodes[Now << 1].Add_Mark += Nodes[Now].Add_Mark;
		Nodes[Now << 1 | 1].Add_Mark += Nodes[Now].Add_Mark;
		Nodes[Now].Add_Mark = 0;
	}

	inline void Update(int Now)
	{
		Push_Down(Now << 1);
		Push_Down(Now << 1 | 1);
		Nodes[Now].Sum = Nodes[Now << 1].Sum + Nodes[Now << 1 | 1].Sum;
		Nodes[Now].Size = Nodes[Now << 1].Size + Nodes[Now << 1 | 1].Size;
	}

	inline void Build(int Left, int Right, int Now)
	{
		Nodes[Now].Left = Left;
		Nodes[Now].Right = Right;
		Nodes[Now].Mid = Left + Right >> 1;
		if(Left == Right)
		{
			Nodes[Now].Sum = Time[Left];
			Nodes[Now].Size = 1;
			return;
		}
		Build(Left, Nodes[Now].Mid, Now << 1);
		Build(Nodes[Now].Mid + 1, Right, Now << 1 | 1);
		Update(Now);
	}

	inline void Delete(int Location, int Now)
	{
		Push_Down(Now);
		if(Nodes[Now].Left == Nodes[Now].Right)
		{
			Nodes[Now].Size = Nodes[Now].Sum = 0;
			return;
		}
		int i = Location > Nodes[Now].Mid;
		Delete(Location, Now << 1 | i);
		Update(Now);
	}

	inline void Change(long long Value)
	{
		Nodes[1].Add_Mark += Value;
	}

	inline long long Ask(int Location, int Now)
	{
		Push_Down(Now);
		if(Location == Nodes[Now].Right)
			return Nodes[Now].Sum;
		if(Location > Nodes[Now].Mid)
			return Ask(Nodes[Now].Mid, Now << 1) + Ask(Location, Now << 1 | 1);
		else
			return Ask(Location, Now << 1);
	}
}Tree;

int main()
{
	cin >> N >> M;
	for(int i = 1; i <= M; ++i)
		D[i].Time = Time[i] = Get_Int(), D[i].Pos = i;
	for(int i = 1; i <= N; ++i)
	{
		A[i].a = Get_Int();
		A[i].b = Get_Int();
		A[i].Pos = i;
	}
	sort(A + 1, A + N + 1);
	sort(D + 1, D + M + 1);
	int Now = 1;
	Tree.Build(1, M, 1);
	for(int i = 1; i <= N; ++i)
	{
		while(Now <= M && A[i].a >= D[Now].Time)
			Tree.Delete(D[Now++].Pos, 1);
		Tree.Change(A[i - 1].a - A[i].a);
		int Left = 0, Right = M + 1, Mid = Left + Right >> 1;
		while(Mid != Left && Mid != Right)
		{
			if(Tree.Ask(Mid, 1) >= A[i].b)
				Right = Mid;
			else
				Left = Mid;
			Mid = Left + Right >> 1;
		}
		Ans[A[i].Pos] = Right;
		if(Right == M + 1)
			Ans[A[i].Pos] = 0;
	}
	for(int i = 1; i <= N; ++i)
		printf("%d ", Ans[i]);
	return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值