CF 589G 树状数组

题目链接:http://codeforces.com/contest/589/problem/G


好久没做过树状数组的了,也应该开始练练数据结构了

题意:给你m个工作日,每个工作日有工作时间,给你n个员工,每个员工每天工作有准备工作时间和实际工作时间,每天实际工作时间之前必须做完准备工作时间,问这个员工最早要第几天完成

思路:一开始的想法只想到把员工的准备工作时间从小到大排序,其他的想法还真没有。。。后来想到了可以弄一个队列,再弄一个树状数组,表示第1到第i天的所有的工作时间的和,因为员工的准备工作时间已经从小到大排序了,所以每次只要把队列中小于该员工准备工作时间的工作时间删去,在树状数组中更新就好了。之后在二分工作日,假设当前列举到第mid天,那么知道了1到mid天的所有时间和,但是这个时间和是所有的时间和,应该还有减去准备时间 * 天数的才是真正的工作时间,所以还要再弄一个树状数组记录1到mid天有几天被删去了,这样就能计算出1到mid天有几天是实际工作的。


#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn = 200005;

ll sum[maxn];
int _sum[maxn];
ll ans[maxn];
struct ppp
{
	int id,pre,v;
	void read(int _id){
		scanf("%d%d",&pre,&v);
		id = _id;
	}
	bool operator < (const ppp & nex)const{
		return pre < nex.pre;
	}
}ori[maxn];
vector<int> vec[maxn * 5];
int n,m;
int lowbit(int x)
{
	return x & -x;
}
void sub(int x,int v)
{
	while(x <= m)
	{
		sum[x] -= v;
		x += lowbit(x);
	}
}
ll query(int x)
{
	ll ans = 0;
	while(x > 0)
	{
		ans += sum[x];
		x -= lowbit(x);
	}
	return ans;
}
void _add(int x)
{
	while(x <= m)
	{
		_sum[x] += 1;
		x += lowbit(x);
	}
}
int _query(int x)
{
	int ans = 0;
	while(x > 0)
	{
		ans += _sum[x];
		x -= lowbit(x);
	}
	return ans;
}
int cal(int i)
{
	int l = 1, r = m;
	int ret = 0;
	while(l <= r)
	{
		int mid = (l + r) >> 1;
		int cnt = mid - _query(mid);
		ll temp = query(mid) - ori[i].pre * 1LL * cnt;
		if(temp >= ori[i].v){
			r = mid - 1;
			ret = mid;
		}
		else l = mid + 1;
	}
	return ret;
}

void init()
{
	int pos = 0;
	mem(sum,0);
	mem(_sum,0);
	for(int i = 0;i < maxn * 5;i ++)vec[i].clear();
	for(int i = 1,a;i <= m;i++){
		scanf("%d",&a);
		int x = i;
		while(x <= m){
			sum[x] += a;
			x += lowbit(x);
		}
		vec[a].push_back(i);
	}
	for(int i = 1;i <= n;i++)ori[i].read(i);
	sort(ori + 1,ori + 1 + n);
	for(int i = 1;i <= n;i++){
		while(pos <= ori[i].pre){
			for(int j = 0;j < vec[pos].size();j++)
			{
				sub(vec[pos][j],pos);
				_add(vec[pos][j]);
			}
			pos++;
		}
		ans[ori[i].id] = cal(i);
	}
}
int main()
{
	while(cin>>n>>m)
	{
		init();
		for(int i = 1;i <= n;i++)
			cout<<ans[i]<<" ";
		cout<<endl;
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值