Codeforces Round #625 (Div. 2, based on Technocup 2020 Final Round)
我好菜
A
将败场场次均摊给每一个胜场就可以最小化最大值了。
B
对于
i
i
i位置(
0
0
0开始编号)的元素
a
i
a_i
ai,我们可以将其看作是以
a
i
−
i
a_i - i
ai−i开头的一个公差为1的等差序列中的一项。因为这个等差序列可以空缺若干项,所以用一个桶维护所有的开头,读入一个元素就加入到相应的桶中,最后查询所有的开头获取最大值即可。
场上SB了…为什么会觉得开头只能是正数…这个等差序列是可以空缺的…所以哪怕开头成为负数只要不出现仍然不会影响答案的正确性…rank300->rank900 为了对负数存桶,将所有的开头加上一个偏移量+2e5即可。
C
根据题意,那些越靠后的字母应该优先考虑是否能被移除。
这是因为如果我们当前消去的元素不是最靠后的元素,那么最靠后的元素就有可能无法被消除;但如果我们消去的元素是最靠后的元素,它既不会对之后的(因为没有更靠后的元素)元素能否被消除造成影响,还能增加靠前元素被消除的可能(将原本被割断的元素们相邻)。所以优先删除靠后元素显然是最优的。
得到这种贪心策略后,我们只需要优先考虑删除更靠后的元素。
要注意的是一段相同元素应该被看作是一个带权元素,所以还要进行化线为点的操作。
另外,当我们目前考虑的区间可以被移除时,还要考虑左右区间是否可以被合并成一段相同的区间。
遍历所有的最大值发生n次,对每一个最大值有可能要删除并拼接两端区间,时间复杂度也为
O
(
n
)
O(n)
O(n),所以总共时间复杂度为
O
(
n
2
)
O(n^2)
O(n2)。在100的范围内没有问题,数组处理即可,不需要用链表。
D
反向存图,先dijstra求得终点到所有点的距离。然后考虑正向图中给出的路径:
记mini为最少重制的次数,maxi为最多重制的次数。
- 如果下一个节点不是最短路之一,
mini++ - 如果下一个节点不是最短路之一 或 下个节点是最短路之一并且仍有另一条最短路,
maxi++
处理完所有路径即可。
补题
E
做的时候时间大概还有半小时…
然后不知道为什么sb了读题的时候知道可以打多个怪写的时候忽然想到一个假题意以为是只打一个怪就开始写了…?? 写了15分钟WA了3次后发现不对了…
正确算法: 线段树
最后15分钟思考的时候想到了线段树,但是好像没有想到要怎么维护…
一种可行的算法:
先把所有的武器、盔甲预处理出gong[maxx], fang[maxx]表示攻击(防御)力至少为i的最小花费。
然后用fang数组初始化最值线段树。
再根据从小到大进行遍历所有伤害值;另设一个指针在每次遍历伤害的时候不断将那些防御力严格低于当前伤害的怪物信息加入极值线段树,加入方法是将线段树中[当前怪物攻击力 + 1, maxsize]区间加上当前怪物的奖励;当所有可被当前攻击击败的怪物信息加入ST中后,尝试用-gong[i] + st[1]来更新答案即可。
因为我们递增地遍历攻击力,所以之前那些更弱的怪一定可以被当前攻击力打败,从而其奖励就在线段树中不断累加;对每一个攻击力查找一个收益最高的防御力即可。
小坑点
如果用0x3f来初始化inf会WA13…所以在对inf设定的时候要仔细考虑最大的范围。
还有就是maxsize要开的比1000000大一点,不然如果出现[1000001, 1000000]就不太好办了。
const int inf = 2147483647;
#define ls (p << 1)
#define rs (p << 1 | 1)
#define mid ((l + r) >> 1)
#define maxsize 1000005
const int maxn = 2e5 + 10;
const int maxx = 1e6 + 10;
int n, m, k;
int gong[maxx], fang[maxx];
struct node{int x, y, z;}mon[maxn];
bool cmp(node a, node b){
return a.x < b.x;
}
inline ll max(ll x, ll y){return x > y? x: y;}
inline int min(int x, int y){return x < y? x: y;}
ll st[maxx << 2], tag[maxx << 2];
void up(int p){ st[p] = max(st[ls],st[rs]);}
void down(int p, int l, int r){
if(tag[p] == 0) return;
st[ls] += tag[p]; st[rs] += tag[p];
tag[ls] += tag[p]; tag[rs] += tag[p];
tag[p] = 0;
}
void build(int p, int l, int r){
if(l == r){st[p] = -fang[l]; return;}
build(ls, l, mid); build(rs, mid + 1, r);
up(p);
}
void update(int p, int l, int r, int L, int R, int k){
if(L <= l && r <= R){
st[p] += k; tag[p] += k;
return;
}
down(p, l, r);
if(L <= mid) update(ls, l, mid, L, R, k);
if(R > mid) update(rs, mid + 1, r, L, R, k);
up(p);
}
int main(){
// Fast;
for(int i = 0; i < maxx; i++){fang[i] = inf; gong[i] = inf;}
scanf("%d%d%d", &n, &m, &k);
for(int i = 0, x, y; i < n; i++){
scanf("%d %d", &x, &y);
gong[x] = min(gong[x], y);
}
for(int i = 0, x, y; i < m; i++){
scanf("%d %d", &x, &y);
fang[x] = min(fang[x], y);
}
for(int i = maxx - 2; i >= 1; i--){
gong[i] = min(gong[i], gong[i + 1]);
fang[i] = min(fang[i], fang[i + 1]);
}
build(1, 1, maxsize);
for(int i = 0; i < k; i++) scanf("%d %d %d", &mon[i].x, &mon[i].y, &mon[i].z);
std::sort(mon, mon + k, cmp);
ll ans = -inf;
int p = 0;
for(int i = 1; i <= maxsize; i++){
ll temp = -gong[i];
while(p < k && mon[p].x < i){
update(1, 1, maxsize, mon[p].y + 1, maxsize, mon[p].z);
p++;
}
temp += st[1];
ans = max(ans, temp);
}
printf("%lld\n", ans);
return 0;
}
本文详细解析了Codeforces Round #625的比赛题目,包括A、B、C、D、E五个问题的解题思路和算法实现,涉及贪心策略、等差序列、图论和线段树等数据结构与算法知识。
603

被折叠的 条评论
为什么被折叠?



