Problem C - Medical Checkup
题意(大概):用n个人要排队体检,都必须从第1个项目开始体检,依次体检下去。其中第2个人必须在第1个人检查完第1个项目之后开始检查第1个项目。某个人体检每个项目的时间花费都是确定的。问t时刻每个人正在体检的项目是什么,假如t时刻某人已经体检完成,则输出他正在准备体检的项目。
先考虑一个简单的问题,从第1个人开始,那么答案就直接是:(x_1=lfloorfrac{t_1}{a_1} floor+1),这个应该很好解释……直接取下整(已经完全体检完成的项目),然后+1(正在进行/准备进行的项目)。
然后(t_2=t_1-a_1)(后面的人的时间都被第一个人的体检占去了)
第2个人就复杂一些,首先他是从第1个人把第1个项目释放之后才开始体检,假如他不比第1个人的时间长,那么他就可以一直跟着第1个人做,最后特殊判断一下他有没有体检完成。画个图就可以看懂。反之,假如他比第1个人长,那么对他来说就不需要等,直接套第1个人的算法就可以得结果。
回过头看,其实第1个人不加特判也可以这样做。
也就是:
(maxa_i=max(maxa_{i-1},a_i))
(x_i=lfloorfrac{t_i}{maxa_i} floor+1+[t_i; mod ; maxa_i ge a_i])
(t_i=t_{i-1}-a_i)
上面的算法有个致命漏洞就是(t_i=t_{i-1}-max{a_i})可能会出负数,WA掉一发,修正之后是:
(max{a_i}=max(maxa_i,a_i))
(x_i=lfloorfrac{t_i}{maxa_i} floor+1+[t_i; mod ; maxa_i ge a_i])
(t_i=t_{i-1}-min(t_{i-1},a_i))
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int n, t;
while(~scanf("%d%d", &n, &t)) {
int a, maxa = 0;
for(int i = 1; i <= n; ++i) {
scanf("%d", &a);
maxa = max(maxa, a);
printf("%d
", t / maxa + 1 + (t % maxa >= a));
t -= min(t, a);
}
}
}
Problem I - Starting a Scenic Railroad Service
题意:有n个人坐火车,每个人有个要坐的区间[L,R],有两种出票方法:一种是每个人先到先选,另一种是接收全部人的请求后统一安排。先到先选意思是,当他开始订票的时候,他这个区间里有空位的话,那么他可以选任意一个空位。求最小的座位安排,使得无论订票的顺序是什么,每个人都有得坐。
首先说第二种,貌似这种就是每次给[L,R]区间覆盖+1,最后取所有区间的最大值,现场时我用的一个线段树来做,实际上只需要差分最后统计一波前缀和。
然后是第一种,总是能够构造一种方法,使得最坏的情况为某个人乘车区间内车上乘客的总数,非常简单,假设少1个位,那么让其他人每个人都选一个位坐那里,然后这个长区间的就没办法坐了。而不会需要更多的位置,这个应该是很显然的。这个可以用两个数组pre和suf表示每个时间前结束的人数、每个时间后开始的人数,那么pre[L]和suf[R]就是不会和这个人有相交的人数,剩下的都是相交的。
所以这道题是可以开到1e7的。
也有人是用二分+主席树做的,感觉很沙雕……太复杂了,还是上面简单。
注意各种+1,-1的情况就可以了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
const int MAXN = 100000;
int st[(MAXN << 2) + 5], lazy[(MAXN << 2) + 5];
inline void PushUp(int o) {
st[o] = max(st[ls], st[rs]);
}
inline void PushDown(int o) {
if(lazy[o]) {
st[ls] += lazy[o];
lazy[ls] += lazy[o];
st[rs] += lazy[o];
lazy[rs] += lazy[o];
lazy[o] = 0;
}
}
inline void Update(int o, int l, int r, int ql, int qr, int v) {
if(ql <= l && r <= qr) {
st[o] += v;
lazy[o] += v;
} else {
PushDown(o);
int m = (l + r) >> 1;
if(ql <= m)
Update(ls, l, m, ql, qr, v);
if(qr >= m + 1)
Update(rs, m + 1, r, ql, qr, v);
PushUp(o);
}
}
inline int QueryMax(int o, int l, int r, int ql, int qr) {
if(ql <= l && r <= qr) {
return st[o];
} else {
PushDown(o);
int m = (l + r) >> 1, res = 0;
if(ql <= m)
res = QueryMax(ls, l, m, ql, qr);
if(qr >= m + 1)
res = max(res, QueryMax(rs, m + 1, r, ql, qr));
return res;
}
}
int L[200005], R[200005];
int pre[MAXN + 5], suf[MAXN + 5];
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int n;
while(~scanf("%d", &n)) {
int maxans = 0, minans = n;
memset(st, 0, sizeof(st[0]) * (n + 1));
memset(lazy, 0, sizeof(lazy[0]) * (n + 1));
memset(pre, 0, sizeof(pre));
memset(suf, 0, sizeof(suf));
for(int i = 1; i <= n; ++i) {
scanf("%d%d", &L[i], &R[i]);
pre[R[i] - 1]++;
suf[L[i]]++;
Update(1, 1, MAXN, L[i], R[i] - 1, 1);
}
for(int i = 1; i <= MAXN; ++i)
pre[i] += pre[i - 1];
for(int i = MAXN; i >= 1; --i)
suf[i] += suf[i + 1];
for(int i = 1; i <= n; ++i)
maxans = max(maxans, n - pre[L[i] - 1] - suf[R[i]]);
minans = min(minans, QueryMax(1, 1, MAXN, 1, MAXN));
printf("%d %d
", maxans, minans);
}
}
假如用差分的话就不需要+1-1了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 200000, MAXP = 100000;
int L[MAXN + 5], R[MAXN + 5];
int pre[MAXP + 5], suf[MAXP + 5], dif[MAXP + 5];
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int n;
while(~scanf("%d", &n)) {
int ans1 = 0, ans2 = 0, sum = 0;
memset(dif, 0, sizeof(dif));
memset(pre, 0, sizeof(pre));
memset(suf, 0, sizeof(suf));
for(int i = 1; i <= n; ++i) {
scanf("%d%d", &L[i], &R[i]);
++pre[R[i]], ++suf[L[i]];
++dif[L[i]], --dif[R[i]];
}
for(int i = 1; i <= MAXP; ++i)
pre[i] += pre[i - 1];
for(int i = MAXP; i >= 1; --i)
suf[i] += suf[i + 1];
for(int i = 1; i <= n; ++i)
ans1 = max(ans1, n - pre[L[i]] - suf[R[i]]);
for(int i = 1; i <= MAXP; ++i) {
sum += dif[i];
ans2 = max(ans2, sum);
}
printf("%d %d
", ans1, ans2);
}
}
pre[x] 表示x位置及其之前就结束的, suf[x] 表示x位置及其之后才开始的。那么不相交的显然就是 n - pre[L[i]] - suf[R[i]] 。而差分就更简单了,因为是线段覆盖,所以就统一用每段的左端点来表示。
这题假如数据范围出大一点的话,就需要离散化,离散化之后也是O(n),(没有离散化是O(n+maxp))。