题目链接:http://codeforces.com/gym/101190/attachments
题意:一个公共三轮车车站可以提供给一些人租车,但是在开始时刻,公园里有所少量车这个是不确定的,然后现在有n个信息和q个询问,信息有两种形式+ a b代表在a时间有b个人还车,另外一种是 - a b代表在a时间有b个人借车,这些人借车是要排队的,如果当前没有车,则只能等待,对于每一个询问给定了初始时刻车站里面有多少量车,然后要询问的是对于每一个可能的初始值的等待时间,如果等待时间是不确定的话,输出INFINITY。
解法:首先是一个单点更新的问题,可是我们发现一次更新之后会影响下一次的查询,或者你更新之后又可以回到之前的版本,所以可持久化?瞎几把yy之后,我们不妨来分析一下样例,可以观察到什么时候是要租车的呢?首先输入的信息用 op t[i] a[i]来表示一下,t[i]代表时间戳,a[i]代表人或者车的数量(取决于操作),第二种操作意味着要减少a[i]量车,如果车你的数量减小到<=0那么久只能等待了。并且当前要等待的时间是多少呢?我们容易把它预处理出来,下面的s[i]就是人或者车的数量的前缀和,显然为正代表当前时间不需要租车,否则需要租车。
for(int i = 1; i <= n; i++){ s[i] = s[i-1] + a[i]; if(i < n) t[i] = t[i+1] - t[i]; }
处理出这个也不能解决这个问题,接下来显然当s[i]>0的那些时间段是不用考虑的,你想啊,当前车的数量够用,肯定傻子才会等待吧。所以我们把需要等待的总时间以及等待的时间段的长度记录下来
long long sum1 = 0, sum2 = 0;
for(int i = 1; i <= n; i++){
if(s[i] < 0){
b[++cnt] = i;
sum1 += 1LL*(-s[i])*t[i];
sum2 += t[i];
}
}
接下来就是想到我们在线很难做那么我们可以离线做啊,所以就有了询问离线的思路了。我们会想离线该怎么做呢?容易想到我们会按照x来排序,离线是为了能用到之前处理的信息,而不是无序重来一遍,这样显然是可以降低复杂度的吧。所以我们会想到按照x从小到大排序,这里的x就是第初始的车的数量,想一下吧,当前x比较小的时候你覆盖了一些之前等待的时间区间,那么下次你的x比现在的大,也必然可以覆盖上次的区间,所以就不用重复扫描,自然大大降低了复杂度。
struct node{
int x, id;
bool operator < (const node &rhs) const{
return x < rhs.x;
}
}c[maxn];
在询问离线的时候我已经提到了,要利用之前的信息,所以我们也要对那些需要等待的时间段进行排序,每一个时间段都有一个值代表当前等待的人数,这里是负数所以从大到小排,比如-2>-3,那么2会排在3的前面,那么你x增大,显然也是可以覆盖当前区间能覆盖的每一条线段的,然后我们按照这个思路去模拟一发就可以了。注意不确定的条件就是当当前的x+s[n]<0的时候,这个时候显然车都不够。
sort(c + 1, c + q + 1);
for(int i = 1; i <= q; i++){ int x = c[i].x; while(j <= cnt && (-s[b[j]]) <= x){ sum1 -= 1LL*(-s[b[j]]) * t[b[j]]; sum2 -= t[b[j]]; ++j; }
if(s[n] + x < 0){ ans[c[i].id] = -1; }
else{ ans[c[i].id] = sum1 - x * sum2; }
}
完整码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
int n, q;
int a[maxn], t[maxn];
int s[maxn];
int b[maxn], cnt;
long long ans[maxn];
struct node{
int x, id;
bool operator < (const node &rhs) const{
return x < rhs.x;
}
}c[maxn];
bool cmp(int i, int j){
return s[i] > s[j];
}
int main()
{
freopen("expect.in", "r", stdin);
freopen("expect.out", "w", stdout);
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; i++){
char op[5];
scanf("%s%d%d", op, &t[i], &a[i]);
if(op[0] == '-') a[i] = -a[i];
}
//处理出初始的信息
for(int i = 1; i <= n; i++){
s[i] = s[i-1] + a[i];
if(i < n) t[i] = t[i+1] - t[i];
}
// for(int i = 1; i <= n; i++){
// printf("s[%d] = %d, t[%d] = %d\n", i, s[i], i, t[i]);
// }
long long sum1 = 0, sum2 = 0;
for(int i = 1; i <= n; i++){
if(s[i] < 0){
b[++cnt] = i;
sum1 += 1LL*(-s[i])*t[i];
sum2 += t[i];
}
}
sort(b + 1, b + cnt + 1, cmp);
int j = 1;
long long k = 0;
for(int i = 1; i <= q; i++){
scanf("%d", &c[i].x);
c[i].id = i;
}
sort(c + 1, c + q + 1);
for(int i = 1; i <= q; i++){
int x = c[i].x;
while(j <= cnt && (-s[b[j]]) <= x){
sum1 -= 1LL*(-s[b[j]]) * t[b[j]];
sum2 -= t[b[j]];
++j;
}
if(s[n] + x < 0){
ans[c[i].id] = -1;
}
else{
ans[c[i].id] = sum1 - x * sum2;
}
}
for(int i = 1; i <= q; i++){
if(ans[i] == -1) printf("INFINITY\n");
else printf("%lld\n", ans[i]);
}
return 0;
}