CF76
CF76A
CF76A题意
给出一个图( n ≤ 2 × 1 0 2 , m ≤ 5 × 1 0 4 n \le 2\times 10^2,m\le 5\times 10^4 n≤2×102,m≤5×104),每条边有两个属性 ( g i , s i ) (g_i,s_i) (gi,si),给出常数 G , S G,S G,S,求一个生成树 T T T 使得 G × max ( g i ) + S × max ( s i ) G \times \max(g_i) + S \times \max(s_i) G×max(gi)+S×max(si) 最小,求出这个值。
CF76A题解
一眼顶针,鉴定为乱搞题
首先先将所有边按照 g i g_i gi 排序,然后从小到大枚举 s i s_i si,每次只加入 s j > s i s_j > s_i sj>si 的边,然后跑 k r u s k a l kruskal kruskal。
但是这样显然是 O ( m 2 ) O(m^2) O(m2) 的,考虑优化。
注意到如果在一次 k r u s k a l kruskal kruskal 的时候,一条边没有用到,那么以后就永远用不到了,因为选这条边显然不会优于之前的某种选法。
而且 n n n 又这么小,每次只需要维护 n − 1 n - 1 n−1 条边就好了。
然后就可以对每个 s i s_i si 算出对应的答案啦。
于是就可以 O ( n m ) O(nm) O(nm) 快乐地过啦!
CF776A优化
- 注意到刚开始至少需要选 n − 1 n - 1 n−1条边,可以先二分一个答案,然后从这个位置开始枚举 s i s_i si。
- 维护边用的集合可以用 m u l t i s e t multiset multiset。
CF76A代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){
x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 2e2 + 10, maxm = 5e4 + 10;
int n, m;
int G, S;
struct edge{
int u, v, g, s, nexte;
edge(int u = 0,int v = 0,int g = 0,int s = 0,int ne = 0):u(u),v(v),g(g),s(s),nexte(ne){
}
friend bool operator < (edge a,edge b){
return a.s < b.s;}
}e[maxm],edg;
int gold[maxm];
int fa[maxn];
int getf(int x){
return fa[x] == x ? x : fa[x] = getf(fa[x]);}
multiset<edge> E;
typedef multiset<edge>::iterator iter;
bool check(int mid){
int cnt = 1;
for(int i = 1;i <= n;i++){
fa[i] = i;}
for(iter it = E.begin();it != E.end();it++){
edg = *it;int fu = getf(edg.u), fv = getf(edg.v);
if(fu == fv || edg.g > mid)continue;
fa[fu] = fv;cnt++;
}
return cnt >= n;
}
signed main(){
int u, v, g, s, ans = 0x3f3f3f3f3f3f3f3f;
n = read(); m = read();G = read(); S = read();
for(int i = 1;i <= m;i++){
u = read(); v = read(); g = read(); s = read();
gold[i] = g;
e[i] = edge(u,v,g,s);E.insert(e[i]);
}
sort(gold + 1,gold + 1 + m);
int l = 1, r = m, res = -1;
while(l <= r){
int mid = l + r >> 1;
if(check(gold[mid])){
r = mid - 1;res = mid;}
else l = mid + 1;
}
if(res == -1){
puts("-1");return 0;}
for(int point = res;point <= m;point++){
int cnt = 1, maxx = 0;
for(int j = 1;j <= n;j++)fa[j] = j;
for(iter it = E.begin();it != E.end();it++){
edg = *it;int fu = getf(edg.u), fv = getf(edg.v);
if(edg.g > gold[point])continue;
if(fu == fv){
iter tmp = it;tmp--;E.erase(it);it = tmp;}
else{
fa[fu] = fv;maxx = edg.s;cnt++;}
if(cnt == n)break;
}
if(cnt == n)ans = min(ans,maxx * S + gold[point] * G);
}
printf("%lld\n",ans == 0x3f3f3f3f3f3f3f3f ? -1 : ans);
return 0;
}
CF76B
CF76B题意
在平面上给出两条直线 y = Y 0 y = Y_0 y=Y0 和 y = Y 1 y = Y_1 y=Y1。在 y = Y 0 y = Y_0 y=Y0 上有 n n n 只老鼠,第 i i i 只老鼠横坐标为 x 1 , i x_{1, i} x1,i,在 y = Y 1 y = Y_1 y=Y1 上有 m m m 个奶酪,第 i i i 个奶酪横坐标为 x 2 , i x_{2, i} x2,i。已知一只老鼠的捕食策略如下:
1. 1. 1. 如果离这只老鼠最近的奶酪有且只有一个,那么这只老鼠会往这个奶酪移动。
2. 2. 2. 如果有多个奶酪离老鼠距离最近,那么这只老鼠会选择向使所有老鼠中挨饿个数最少的奶酪移动。
每只老鼠的移动速度都是一样的,当某些老鼠到达某个奶酪并且当前奶酪还没有被吃掉时,他们会吃掉奶酪并且不再挨饿。如果某个老鼠在到达奶酪时奶酪已经被吃掉了,那么它不会再进行移动,此时我们认为它处于挨饿状态。
请输出最终处于挨饿状态的老鼠个数。
CF76B题解
考虑贪心的找答案,枚举对于每只老鼠,如果只满足第 1 1 1 中情况就没办法了,但是如果满足第 2 2 2 种情况,那么尽可能的让这只老鼠向左找\
CF76B代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch == '-') f

最低0.47元/天 解锁文章
150

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



