题目描述
输入
输出
输出m行,每行一个整数,代表输入中每次程序变化后系统所需要的空闲内存单位数。
样例输入
2 3
1 4
1 4
2 2 1
2 1 1
1 1 1
样例输出
2
3
1
数据范围
对于30%的数据,有1<=n,m<=1000
对于100%的数据,有1<=n,m<=100000
样例解释
解法
显然存在一种排列顺序,使得代价最小。
考虑这个排列的方式:易得b[i]<=b[j]时,代价最小。
感性证明:
假设当前所需代价为x,初始为0。
把所有代价小于等于x的程序立即完成,然后获得可以获得的内存。
直到没有完成的程序后,立即分配一单位的内存。
这样贪心可以保证正确性。
考虑合并两个程序为一个等价的程序。
显然把所有程序合并起来就是答案。
此外一个重要性质是,合并操作满足结合律。
这样就可以利用线段树来处理程序的合并。
离线处理即可。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define sqr(x) ((x)*(x))
#define ln(x,y) ll(log(x)/log(y))
using namespace std;
const char* fin="ex3571.in";
const char* fout="ex3571.out";
const ll inf=0x7fffffff;
const ll maxn=100007*2,maxt=maxn*4;
ll n,m,i,j,k,l,rank[maxn],b[maxn],d[maxn];
struct prog{
ll get,need,id;
void operator=(const prog &b){
get=b.get;
need=b.need;
id=b.id;
}
prog(){
get=need=id=0;
}
}a[maxn],c[maxt];
prog merge(prog a,prog b){
prog c;
c.need=max(a.need,b.need-a.get);
c.get=a.get+b.get;
return c;
}
bool cmp(prog a,prog b){
return a.need<b.need;
}
void change(ll l,ll r,ll t,ll v,const prog &v1){
ll mid=(l+r)/2;
if (l==r){
c[t]=v1;
return ;
}
if (v<=mid) change(l,mid,t*2,v,v1);
else change(mid+1,r,t*2+1,v,v1);
c[t]=merge(c[t*2],c[t*2+1]);
}
int main(){
scanf("%d%d",&n,&m);
for (i=1;i<=n;i++){
scanf("%d%d",&a[i].get,&a[i].need);
a[i].id=i;
}
for (i=n+1;i<=n+m;i++){
scanf("%d%d%d",&b[i],&k,&l);
a[i].need=l;
a[i].get=k;
a[i].id=i;
}
sort(a+1,a+n+m+1,cmp);
for (i=1;i<=n+m;i++) rank[a[i].id]=i;
for (i=1;i<=n;i++) change(1,n+m,1,rank[i],a[rank[i]]),d[i]=rank[i];
for (i=n+1;i<=n+m;i++){
change(1,n+m,1,d[b[i]],c[0]);
change(1,n+m,1,rank[i],a[rank[i]]);
d[b[i]]=rank[i];
printf("%lld
",c[1].need);
}
return 0;
}
启发
对于这类不同排列不同答案的问题。
可以考虑合并元素;
如果元素满足结合律,那么就可以使用线段树进行合并。