Codeforces Round #610 (Div. 2)
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 就不会出任何事。
离散化的写法好像也没高明到哪里去:
int n, T, a, b;
struct Problem {
int t, d;
bool operator<(const Problem& p)const {
return t < p.t;
}
} p[200005];
int t[200005];
int cura[200005], curb[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;
}
for(int i = 1; i <= n; ++i)
t[i] = p[i].t;
sort(t + 1, t + 1 + n);
int tn = unique(t + 1, t + 1 + n) - (t + 1);
for(int i = 1; i <= tn; ++i)
cura[i] = 0, curb[i] = 0;
for(int i = 1; i <= n; ++i) {
p[i].t = lower_bound(t + 1, t + 1 + tn, p[i].t) - t;
if(p[i].d == 0)
++cura[p[i].t];
else
++curb[p[i].t];
}
for(int i = 1; i <= tn; ++i) {
cura[i] += cura[i - 1];
curb[i] += curb[i - 1];
}
int ans = 0;
for(int i = 1; i <= tn; ++i) {
int curt = t[i] - 1;
if(curt >= (1ll * cura[i - 1] * a + 1ll * curb[i - 1] * b)) {
int rest = curt - (1ll * cura[i - 1] * a + 1ll * curb[i - 1] * b);
int cnta = min(suma - cura[i - 1], rest / a);
//这里并不可能溢出
rest -= cnta * a;
int cntb = min(sumb - curb[i - 1], rest / b);
//这里并不可能溢出
rest -= cntb * b;
ans = max(ans, cura[i - 1] + curb[i - 1] + cnta + cntb);
}
}
printf("%d\n", ans);
}
D - Enchanted Artifact
交互题,卡的还蛮紧,很多人都想出了n+3的解法。
题意:猜一段只有'a'和'b'的字符串,连长度n未知,只能猜至多n+2次,每次jury返回一个值告诉你你猜的字符串和答案的“修改距离”(最短的使用替换、添加、删除你猜的串t,使得t好答案串s两个字符串相等),猜中0则成功。
题解:首先处理掉数据量较小的情况?其实统一的解法里面不需要这么复杂。
1、先只给一个字母'a',回答res。
当res=0时,退出。
当字符串为全a"aaa...a"时,长度为res+1。
当字符串为全b"bbb...b"时,长度为res。
当字符串为ab混合时,长度也是res+1。
2、设len=res+1,给出长len的字符串全a"aaa...a",回答res。
当字符串为全a"aaa...a"时,res=0,退出。
当字符串为全b"bbb...b"时,res=len,再输出长度为len-1的全b串,走流程退出。
当字符串为ab混合时,答案长度为len,res=b的数量。
3、记pres=res,则pres就是b的数量。问长len的字符串"baaa...a",回答res。
当res<pres,则首字母确实是b。
否则,首字母是a。
确定首字母之后循环执行这一步。
int getAnswer() {
fflush(stdout);
int res;
scanf("%d", &res);
if(res == 0)
exit(0);
return res;
}
char s[305];
void test_case() {
int len, res, pres;
puts("a");
res = getAnswer();
len = res + 1;
for(int i = 1; i <= len; ++i)
s[i] = 'a';
puts(s + 1);
res = getAnswer();
if(res == len) {
for(int i = 1; i <= len - 1; ++i)
s[i] = 'b';
s[len] = '\0';
puts(s + 1);
res = getAnswer();
exit(-1);
}
pres = res;
for(int i = 1; i <= len; ++i) {
s[i] = 'b';
puts(s + 1);
res = getAnswer();
if(res >= pres)
s[i] = 'a';
}
exit(-1);
}
这个WA5了,想想。少了一句赋值 pres = res; ,加上之后WA67了???真就FST警告。原来是提问串>300的长度了,我晕了,还有这种细节的吗?
int getAnswer() {
fflush(stdout);
int res;
scanf("%d", &res);
if(res == 0)
exit(0);
return res;
}
char s[305];
void test_case() {
int len, res, pres;
puts("a");
res = getAnswer();
if(res == 300) {
for(int i = 1; i <= res; ++i)
s[i] = 'b';
puts(s + 1);
res = getAnswer();
exit(-1);
}
len = res + 1;
for(int i = 1; i <= len; ++i)
s[i] = 'a';
puts(s + 1);
res = getAnswer();
if(res == len) {
for(int i = 1; i <= len - 1; ++i)
s[i] = 'b';
s[len] = '\0';
puts(s + 1);
res = getAnswer();
exit(-1);
}
pres = res;
for(int i = 1; i <= len; ++i) {
s[i] = 'b';
puts(s + 1);
res = getAnswer();
if(res >= pres)
s[i] = 'a';
pres = res;
}
exit(-1);
}
还好区域赛不搞这种交互题。
看了一下学长的,直接问300个a,那么a的数量就是cnta=300-res,然后问cnta个a,那么b的数量就是cntb=res。这时假如是全a或者全b要特判一下。
char s[305];
int getAnswer(int len) {
for(int i = 1; i <= len; ++i)
putchar(s[i]);
putchar('\n');
fflush(stdout);
int res;
scanf("%d", &res);
if(res == 0)
exit(0);
return res;
}
void test_case() {
int len = 0, res, pres;
for(int i = 1; i <= 300; ++i)
s[i] = 'a';
res = getAnswer(300);
len = 300 - res;
if(len == 0) {
s[1] = 'b';
res = getAnswer(1);
for(int i = 1; i <= res + 1; ++i)
s[i] = 'b';
res = getAnswer(res + 1);
exit(-1);
}
res = getAnswer(len);
len += res;
pres = res;
for(int i = 1; i <= len; ++i) {
s[i] = 'b';
res = getAnswer(len);
if(res >= pres)
s[i] = 'a';
pres = res;
}
exit(-1);
}