★★☆ 输入文件:classrooms.in
输出文件:classrooms.out
简单对比
时间限制:1 s 内存限制:128 MB
【题目描述】
在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要 向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。
面对海量租借教室的信息,我们自然希望编程解决这个问题。
我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借。共有m份 订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租 借教室(包括第sj天和第tj天),每天需要租借dj个教室。
我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提 供dj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。
借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教 室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申 请人修改订单。这里的无法满足指从第sj天到第tj天中有至少一天剩余的教室数量不足dj个。
现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改 订单。
【输入格式】
第一行包含两个正整数n,m,表示天数和订单的数量。
第二行包含n个正整数,其中第i个数为ri,表示第i天可用于租借的教室数量。
接下来有m行,每行包含三个正整数dj,sj,tj,表示租借的数量,租借开始、结束分别在 第几天。
每行相邻的两个数之间均用一个空格隔开。天数与订单均用从1开始的整数编号。
【输出格式】
如果所有订单均可满足,则输出只有一行,包含一个整数 0。否则(订单无法完全满足) 输出两行,第一行输出一个负整数-1,第二行输出需要修改订单的申请人编号。
【样例输入】
4 3 2 5 4 3 2 1 3 3 2 4 4 2 4
【样例输出】
-1 2
【输入输出样例说明】
第 1 份订单满足后,4 天剩余的教室数分别为 0,3,2,3。第 2 份订单要求第 2 天到 第 4 天每天提供 3 个教室,而第 3 天剩余的教室数为 2,因此无法满足。分配停止,通知第 2 个申请人修改订单。
【数据范围】
对于 10%的数据,有1 ≤ n,m ≤ 10;
对于 30%的数据,有1 ≤ n,m ≤ 1000;
对于 70%的数据,有1 ≤ n,m ≤ 10^5;
对于 100%的数据,有1 ≤ n,m ≤ 10^6,0 ≤ ri,dj ≤ 10^9,1 ≤ sj ≤ tj ≤ n。
维护区间最小值:
//直接线段树区间查询最小值的话是过不了的 #include <iostream> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; const int N = 1e6 + 10; #define lson jd << 1 #define rson jd << 1 | 1 #define oo 999999999 #define LL long long struct Node{ LL l, r, minn, f; }T[N << 2]; int n, m; LL ans_min; inline int read(){ int x = 0; char c = getchar(); while(c < '0' || c > '9') c = getchar(); while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x; } void down(int jd){ T[lson].f += T[jd].f; T[rson].f += T[jd].f; T[lson].minn += T[jd].f; T[rson].minn += T[jd].f; T[jd].f = 0; } void build_tree(int l, int r, int jd){ T[jd].l = l; T[jd].r = r; if(l == r){ T[jd].minn = read(); return ; } int mid = (l + r) >> 1; build_tree(l, mid, lson); build_tree(mid + 1, r, rson); T[jd].minn = min(T[lson].minn, T[rson].minn); } void Sec_ask(int l, int r, int jd, int x, int y){ if(x <= l && r <= y){ ans_min = min(ans_min, T[jd].minn); return ; } if(T[jd].f) down(jd); int mid = (l + r) >> 1; if(x <= mid) Sec_ask(l, mid, lson, x, y); if(y > mid) Sec_ask(mid + 1, r, rson, x, y); } void Sec_g(int l, int r, int jd, int x, int y, int yj){ if(x <= l && r <= y){ T[jd].minn += yj; T[jd].f += yj; return ; } int mid = (l + r) >> 1; if(x <= mid) Sec_g(l, mid, lson, x, y,yj); if(y > mid) Sec_g(mid + 1, r, rson, x, y, yj); T[jd].minn = min(T[lson].minn, T[rson].minn); } int main() { n = read(); m = read(); build_tree(1, n, 1); for(int i = 1; i <= m; i ++) { int want = read(), l = read(), r = read(); ans_min = oo; Sec_ask(1, n, 1, l, r); if(ans_min < want){ printf("-1 %d", i); return 0; } Sec_g(1, n, 1, l, r, -want); } return 0; }
前缀和+二分answer
/* 用某一天的前缀和表示该天需要的教室数 比如一开始数列a是0 0 0 0 0 0 前缀和0 0 0 0 0 0 3到5天需要2的教室 将a[3]+=2,a[6]-=2 数列变为0 0 2 0 0 -2 前缀和变为0 0 2 2 2 0 这样就实现了增加3-5需要的教室数 二分订单数量,判断一下前mid个订单是否可以。具体操作是前缀和处理, 即每读入一个订单就在起始天+要借的房间数量,在结束天的下一天减去要 借的房间数量。然后比较每一天的前缀和与本天总共的房间数的大 小,如果房间数<前缀和,就说明前mid个订单有问题,向前二分;否则就向 后二分。证明:在一个订单的起始天+要借的房间数量,在结束天的 下一天减去要借的房间数量。设一个数组c[i],记录前缀和。读入的数据是 d,s,t C[s]:=c[s]+d;c[t+1]:=c[t+1]-d; 那么如果第i天在s和t之间,那么 前i天的sum{c[i]}中有c[s],相当于已经记下第i天的订单数量了。如果第i天 在t之后,前i天的sum{c[i]}中有c[s]和c[t],因为c[s]+d+c[t+1]-d=c[s]+c[t], 所以这个订单只对s和t中间天数起作用。得证! */ #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int N = 1000010; int n, m, ans; int a[N], d[N], x[N], y[N], s[N], sum; inline int read(){ int x = 0; char c = getchar(); while(c < '0' || c > '9') c = getchar(); while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x; } inline bool judge(int v){ memset(s, 0, sizeof(s)); sum = 0; for(int i = 1; i <= v; i ++) { s[x[i]] += d[i]; s[y[i]+1] -= d[i]; } for(int i = 1; i <= n; i ++) { sum += s[i]; if(sum > a[i]) return 0; } return 1; } int main() { n = read(); m = read(); for(int i=1; i<=n; i++) a[i] = read(); for(int i=1; i<=m; i++) d[i] = read(), x[i] = read(), y[i] = read(); int l = 1, r = m; while(l <= r){ int mid = (l + r) >> 1; if(!judge(mid)) ans = mid, r = mid - 1; else l = mid + 1; } if(!ans) printf("0"); else printf("-1 %d",ans); return 0; }