CF 589G 树状数组

来源:互联网 发布:2017php就业市场饱和 编辑:程序博客网 时间:2024/05/25 23:29

题目链接: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 0
原创粉丝点击