ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CF125E MST Company

2021-07-24 08:31:26  阅读:197  来源: 互联网

标签:度数 int Company MST vec CF125E var include mx


题面

有一张 \(n\) 个点,\(m\) 条边的图,每条边有边权。需要找出一棵生成树,使得 1 号点度数恰好为 \(k\) ,在满足这个条件的前提下生成树的权值和尽量小。

无解输出 −1,否则任意输出一种方案即可

\(1 ≤ n ≤ 5000,0 ≤ m ≤ 100000,0 ≤ k ≤ 5000\)

solution

收获蛮大的一个题

两种做法,但其中一种好像假掉了

  • 破圈

很经典的一种套路,和 std 跑了一上午对拍,为啥我的还是过不了

思路是这样的:

首先把与一号点所连的边全部都删掉,用其他边生成一个森林。

如果生成的联通块的数目 \(x > k\) 显然无解

如果联通块数目 \(x = k\) ,把与一号点相连的边从小到大排个序,然后用它们把所有联通块连起来,形成的树就是答案

如果联通块的数目 \(x < k\) ,同上,先生成一棵树,此时 \(1\) 的度数小于 \(k\) ,考虑与一号点相连的非树边,每次向树中加一条这样的边就会形成一个环,找出环上边权最大的一条边 (不与一号点相连) 删掉,这样 1 的度数就会 + 1,重复这个操作 \(k - x\) 就会保证 1 的度数恰好为 k 了

这个做法细节蛮多的:

怎么找环上最大的边:\(DFS\)​ ,边权转到点上处理

时间复杂度:

\(O (nk \times mlogm)\)​

/*
work by:Ariel_
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
#define ll long long
#define rg register
using namespace std;
const int M = 1e5 + 5;
const int N = 5000 + 5;
int read(){
    int x = 0,f = 1; char c = getchar();
    while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
    return x*f;
}
vector<int> T, vec;
int n, m, k, fa[N], cnt, sum, id[M << 1], del[M << 1], fag[M];
ll mx[N];
int find(int x) {return fa[x] == x ? fa[x] : find(fa[x]);}
struct Edge{int u, v, w, id;}E[M];
struct edge{int u, v, w, nxt;}e[M << 1];
int Ecnt, head[N];
void add_edge(int u, int v, int w) {
    e[++Ecnt] = (edge) {u, v, w, head[u]};
    head[u] = Ecnt;
}
bool cmp(Edge x, Edge y) {return x.w < y.w;}
void forest() {
	for (int i = 1; i <= n; i++) fa[i] = i;
	sort(E + 1, E + m + 1, cmp);
    for (int i = 1; i <= m; i++) {//生成森林 
        int x = find(E[i].u), y = find(E[i].v);
        if (x == y || x == 1 || y == 1) continue; 
        fa[x] = y; T.push_back(E[i].id);//把树边存起来 
        add_edge(E[i].u, E[i].v, E[i].w), add_edge(E[i].v, E[i].u, E[i].w);
        id[Ecnt] = id[Ecnt - 1] = E[i].id;//记录每条边的标号 
    }	
}
void find_max(int x, int f) { 
   for (int i = head[x]; i; i = e[i].nxt) {
    	int v = e[i].v;
    	if (v == f || del[i] || v == 1) continue;
    	   if (e[i].w >= e[mx[x]].w) mx[v] = i;//存的是最大边在图中的编号 
    	   else mx[v] = mx[x];
    	   find_max(v,  x);
	}
}
main(void){
	freopen("a.in", "r", stdin);
    n = read(), m = read(), k = read();
    for (int i = 1; i <= m; i++)
    E[i].u = read(), E[i].v = read(), E[i].w = read(), E[i].id = i;
	forest();
    for (int i = 1; i <= m; i++) {
      if (E[i].u != 1 && E[i].v != 1) continue;
      int u = find(E[i].u), v = find(E[i].v);
      if (u == v) vec.push_back(i);//将 1 号点连出的非树边存起来 
      else fa[u] = v, add_edge(E[i].u, E[i].v, E[i].w), T.push_back(E[i].id), k--;
	}
	
	for (int i = 2; i <= n; i++) if (find(i) != find(i - 1)){printf("-1");return 0;}	    
	
	if (k < 0){printf("-1");return 0;} // 1 号点连出的边一定会大于 k 
	
	for (int i = 1; i <= k; i++) {
	 	if (!vec.size()){ printf("-1");return 0;} 
		memset(mx, 0, sizeof mx);
		for (int j = head[1]; j; j = e[i].nxt) {
		    int v = e[i].v;
			find_max(v, 1);
		} 
		int now = -1, ans = 0x3f3f3f3f3f3f;
	    for (int j = vec.size() - 1; j >= 0; j--)  {
	       int p = vec[j], var = max(E[p].u, E[p].v);//枚举的一条非树边进行交换 
	       if (E[p].w - e[mx[var]].w < ans) ans = E[p].w - e[mx[var]].w, now = j; 
		}
		int p = vec[now], var = E[p].u == 1 ? E[p].v:E[p].u;
		add_edge(E[p].u, E[p].v, E[p].w), add_edge(E[p].v, E[p].u, E[p].w);
		T.push_back(E[p].id);
		fag[id[mx[var]]] = 1, del[mx[var]] = del[mx[var] ^ 1] = 1;
		vec.erase(vec.begin() + now);
	} 
	printf("%d\n", n - 1);
    for (int i = T.size() - 1; i >= 0; i--) if(!fag[T[i]]) printf("%d ", T[i]); 
    return 0;
}

还有一种方法:

另一种方法:考虑将 1 周围所有边权值 +delta,使得权值修正后的图存在点 1 度数为 k 的最小生成树 Delta 可以二分

标签:度数,int,Company,MST,vec,CF125E,var,include,mx
来源: https://www.cnblogs.com/Arielzz/p/15054418.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有