Educational Codeforces Round 77 (Rated for Div. 2)
A - Heating
题意:一个房间,最多装c个加热器,若某个加热器长度为k,则花费为k*k,覆盖这n个长度求最小花费。
题解:首先每个格子只装最多一个,先取min。然后肯定是最平均最好,小平均值是n/d取下整,大平均值是小平均值+1,大平均值的个数是n%d,小平均值的个数即剩下的。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void test_case() {
ll c, n;
scanf("%lld%lld", &c, &n);
c = min(n, c);
ll L = n / c;
ll R = L + 1;
ll cntR = n % c;
ll cntL = (n - (cntR * R)) / L;
ll ans = cntL * (L * L) + cntR * (R * R);
printf("%lld\n", ans);
}
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
scanf("%d", &t);
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
B - Obtain Two Zeroes
题意:两个数x,y,每次可以选择:1、x:=x-d, y:=y-2d 或 2、x:=x-2d, y:=y-d ,d是正整数,求x,y是否可以同时为0。
题解:首先先把两个拉到同一个大小,这是必须的,两个数第一次相遇的时机。当这个时机出现在负数,NO。出现这个时机之后两个数轮流减,直接模3。
void test_case() {
int a, b;
Scanf(a, b);
if(a > b)
swap(a, b);
a -= b - a;
if(a < 0 || a % 3)
return NO();
return YES();
}
C - Infinite Fence
题意:有一串砖,凡是r的倍数而不是b的倍数必须涂红,凡是b的倍数而不是r的倍数必须涂蓝,是公倍数则选一个涂。把涂色的砖选出来之后,问是否一定有连续的k个砖是同一种颜色。
题解:首先,假如两个数有公因子,那么他们都会跳过一系列砖,除掉不影响答案。然后必有他们的gcd为1,若b和a相等,则NO。否则设b更大,那么必须贪心,有b涂就涂b打断r的连续。那么因为公因子是1,那么在某个时刻会存在这样的情形:
br---r---r---rb
br---r---r---r.b
br---r---r---r..b
br---r---r---r...b
也就是一个b之后若干个r周期,然后剩下至多r-1个空格不够再放一个r。已知放k个r需要d=(k-1)*r+1的长度,求b-1的长度是否超过d,注意溢出。然后若b和a相等,也满足此式,合并一起。
void YES() {
//puts("YES");
puts("REBEL");
}
void NO() {
//puts("NO");
puts("OBEY");
}
void test_case() {
ll r, b, k;
Scanf(r, b, k);
if(r > b)
swap(r, b);
ll g = __gcd(r, b);
r /= g, b /= g;
if((b - 1) >= (k - 1) * r + 1)
return YES();
else
return NO();
}
D - A Game with Traps
题意:有m个士兵,t秒,你要带尽可能多的士兵从0去n+1,且他们不能被杀死。路上有一些陷阱,陷阱d[i]会杀死能力比它小的士兵,陷阱位置在l[i],当你走到r[i]时可以拆除它。每次你可以向左或者向右移动,然后立刻拆除当前位置的陷阱。你的士兵必须被你带着移动,且移动到格子时该位置不能有陷阱。
题解:首先可以二分士兵的数量,然后贪心选最强的士兵出来。check的时候也是直接贪心,发现一个规律,就是假如遇到了一个陷阱,则必须要拆除它,经过2次R-L再把士兵带过来。而如果拆的路上还有其他陷阱的话就一路拆过去,因为这些路拆除陷阱至少都要走两次,走过去一起拆了不会更差,拆完一路之后干脆就一起带过来所以走了3倍路程。否则若没有陷阱肯定贪心直接带着士兵右移1格。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int m, n, k, t;
int a[200005];
int R[200005];
vector<int> l[200005];
int d[200005];
bool check(int p) {
int predp = 0, curdp = 0;
for(int i = 1, prei = 0; i <= n + 1; ++i) {
int maxR = -1;
for(auto id : l[i]) {
if(d[id] <= a[p])
continue;
maxR = max(maxR, R[id]);
}
while(i < maxR) {
++i;
for(auto id : l[i]) {
if(d[id] <= a[p])
continue;
maxR = max(maxR, R[id]);
}
}
if(maxR != -1)
curdp = predp + 3 * (i - prei);
else
curdp = predp + 1;
prei = i;
predp = curdp;
}
return curdp <= t;
}
void test_case() {
scanf("%d%d%d%d", &m, &n, &k, &t);
for(int i = 1; i <= m; ++i)
scanf("%d", &a[i]);
for(int i = 1, L; i <= k; ++i) {
scanf("%d%d%d", &L, &R[i], &d[i]);
l[L].push_back(i);
}
sort(a + 1, a + 1 + m, greater<int>());
int l = 0, r = m;
while(1) {
int mid = (l + r) >> 1;
if(l == mid) {
if(check(r)) {
printf("%d\n", r);
return;
}
printf("%d\n", l);
return;
}
if(check(mid))
l = mid;
else
r = mid - 1;
}
}
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
//scanf("%d", &t);
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}
/*
1. 小数据问题退化:
输入为0或1会不会有特殊情况?其他的比如最小环要有三个点,强连通分量缩到最后一个点等等。
2. 内存:
内存的空间有没有开够?有时有反向边,有时有额外新加的边。线段树开了4倍了吗?
可持久化数据结构会不会内存溢出?多组数据时vector会不会翻车?
多组数据有进行初始化吗?memset会不会翻车?
3. 算术溢出:
乘法上溢出了?忘记取模了?输入输出用了%d?让无符号数越到负数了?
4. 习惯:
函数都有指定返回值吗?返回值非void函数的每一个分支都有显式的返回值吗?确定不会进入的分支可以assert一把。
Yes和No有没有反?有没有搞错n和m?离散化之后的cn有没有错?换行和空格要怎么整?priority要把符号反过来。
5. 其他bug:
基环树是树加环,不是链加环。
*/
E - Tournament
题意:有n个选手,n是2的幂。n个人的能力值分别是[1,n],两两配对参加淘汰赛,每次高手会淘汰掉低手,然后高手们参加下一轮。你的朋友也参加,安排适当的顺序让他获胜,你可以贿赂其中的某些人,使得他和你朋友打的时候放水。求最便宜的贿赂总价。
题解:若朋友是天下第一,则0。否则必定会和天下第一打,所以要贿赂天下第一。然后天下第一可以带走[n/2+1,n]这群高手,若朋友在[n/2+1,n]里则直接结束了,否则递归到一半规模的问题。不过这一次不一定要贿赂天下第一(感谢样例2),你可以贿赂一个比天下第一更强的又更便宜的人和天下第一交换。所以维护一个小根堆是最简单的方法,当然也可以动态保留至多logn个人,更贵的就没用了,或者整个归并排序,这样复杂度可能会变小,但是麻烦还容易错。要注意贿赂的人不用重复入队。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[(1 << 18) + 5];
priority_queue<int, vector<int>, greater<int> >pq;
ll solve(int pos, int n) {
if(pos >= n)
return 0;
else {
ll res = 0;
if(pq.empty() || a[n] < pq.top()) {
res += a[n];
for(int i = n / 2 + 1; i < n; ++i)
pq.push(a[i]);
} else {
res += pq.top();
pq.pop();
for(int i = n / 2 + 1; i <= n; ++i)
pq.push(a[i]);
}
res += solve(pos, n / 2);
return res;
}
}
void test_case() {
int n;
scanf("%d", &n);
int p = -1;
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
if(a[i] == -1)
p = i;
}
printf("%lld\n", solve(p, n));
}
int main() {
#ifdef KisekiPurin
freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
int t = 1;
//scanf("%d", &t);
for(int ti = 1; ti <= t; ++ti) {
//printf("Case #%d: ", ti);
test_case();
}
}