題目鏈接: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;
}
}