题目链接: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;
}
}