A - Temporarily unavailable
题意:给一个x轴,从x=a走到x=b,求其中断网的时间的,断网当且仅当你离路由器x=c距离超过r。
题解:找c2<c1的bug找了半天。
void test_case() {
ll a, b, c, r;
scanf("%lld%lld%lld%lld", &a, &b, &c, &r);
if(a > b)
swap(a, b);
ll t = (b - a);
ll c1 = c - r, c2 = c + r;
c1 = max(a, c1);
c2 = min(b, c2);
ll tt = max(0ll, c2 - c1);
printf("%lld\n", t - tt);
}
B1 - K for the Price of One (Easy Version)
见下
B2 - K for the Price of One (Hard Version)
题意:去商店买东西,商店有n个物品,每个物品有自己的价格,商店有个优惠活动,当你买恰好k个东西时可以只为其中最贵的那个付款,求有限的钱中买到的最多的物品数量,你可以多次使用优惠。
题解:不管什么东西先排序,当时想到一个贪心:买了一个物品之后假如这个物品的序号>=k,那么它之前的k-1个物品一定就是被它免费的。剩下的用最前面的零头来凑。但是忽略了一个事实,就是买的东西实际上都是从序号1开始的连续的一段,中间是不会断开的。因为可以将用优惠买的物品的点水平左移,知道恰好把零头的后一个给免费了,这样数量不变,花的钱更少。
int n, p, k;
int a[200005];
ll sum[200005];
ll dp[200005];
void test_case() {
scanf("%d%d%d", &n, &p, &k);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
sort(a + 1, a + 1 + n);
for(int i = 1; i <= n; ++i)
sum[i] = sum[i - 1] + a[i];
for(int i = 1; i < k; ++i)
dp[i] = 0;
for(int i = k; i <= n; ++i)
dp[i] = dp[i - k] + a[i];
int ans = 0;
for(int i = 1; i < k; ++i) {
if(sum[i] <= p)
ans = i;
}
for(int i = k; i <= n; ++i) {
int rp = p - dp[i];
if(rp < 0)
break;
int cnt = 0;
if(i % k)
cnt = upper_bound(sum + 1, sum + 1 + (i % k), rp) - sum - 1;
ans = max(ans, cnt + i / k * k);
}
printf("%d\n", ans);
}
int n, p, k;
int a[200005];
ll dp[200005];
void test_case() {
scanf("%d%d%d", &n, &p, &k);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
sort(a + 1, a + 1 + n);
for(int i = 1; i < k; ++i)
dp[i] = dp[i - 1] + a[i];
for(int i = k; i <= n; ++i)
dp[i] = dp[i - k] + a[i];
int ans = 0;
for(int i = 1; i <= n; ++i) {
if(dp[i] <= p)
ans = i;
}
printf("%d\n", ans);
}
观察了一下题目貌似还可以这么写:
int n, p, k;
int a[200005];
void test_case() {
scanf("%d%d%d", &n, &p, &k);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
sort(a + 1, a + 1 + n);
for(int i = 1; i < k; ++i)
a[i] += a[i - 1];
for(int i = k; i <= n; ++i)
a[i] += a[i - k];
while(a[n] > p)
--n;
printf("%d\n", n);
}
C - Petya and Exam
写完这道题居然能加分,而且出得这么快估计是1800的难度想不到官方也这么觉得。
题意:参加一门课的考试,考试的规则非常的奇怪,和正常中国人见过的都不同。
首先考试只有两种题,一种是简单题,每道题耗时固定为a;另一种是困难题,每道题耗时固定为b,保证b>a。分值都是1。
考试的规则并不只是写多少题得多少分,鼓励提前交卷。假如你没有提前交卷,那么有一部分的题目会列为“必需”,当“必需”的题目没有全部被完成的话,这门课就算0分;否则得到与题数相同的分数,包括“必需”和“非必需”的。
题解:很显然又直接按时间排升序,然后贪心,要在某个题变成必需的前夕进行判断。首先先得把“必需”的时间全部花出去(小心溢出和运算符优先级),剩下的尽可能填“非必需”的简单题,然后尽可能填“非必需”的困难题。这样写会漏一种情况,因为在最后一个变成“必需”之后没有下一个变成“必需”的前夕了,而是考试结束。所以先特判掉全部变成“必需”的情况是不是可以待到考试结束全部做完,然后再贪心。
现在觉得好像把时间离散化的话就不需要使用nxt这种丑陋的写法了。
int n, T, a, b;
struct Problem {
int t, d;
bool operator<(const Problem& p)const {
return t < p.t;
}
} p[200005];
void test_case() {
scanf("%d%d%d%d", &n, &T, &a, &b);
for(int i = 1; i <= n; ++i)
scanf("%d", &p[i].d);
for(int i = 1; i <= n; ++i)
scanf("%d", &p[i].t);
int suma = 0, sumb = 0;
for(int i = 1; i <= n; ++i) {
if(p[i].d == 0)
++suma;
else
++sumb;
}
if(1ll * suma * a + 1ll * sumb * b <= T) {
printf("%d\n", n);
return;
}
sort(p + 1, p + 1 + n);
int ans = 0, cura = 0, curb = 0;
for(int i = 1; i <= n;) {
int curt = p[i].t - 1;
if(curt >= (1ll * cura * a + 1ll * curb * b)) {
int rest = curt - (1ll * cura * a + 1ll * curb * b);
int cnta = min(suma, rest / a);
//这里并不可能溢出
rest -= cnta * a;
int cntb = min(sumb, rest / b);
//这里并不可能溢出
rest -= cntb * b;
ans = max(ans, cura + curb + cnta + cntb);
}
int nxt = i;
while(nxt <= n && p[nxt].t == p[i].t) {
if(p[nxt].d == 0) {
++cura;
--suma;
} else {
++curb;
--sumb;
}
++nxt;
}
i = nxt;
}
printf("%d\n", ans);
}
发现上面的写法里面有两个惊人的乘法没有变 long long 的,有点害怕。以后假如不卡时间可以直接上 long long 就不会出任何事。