zoukankan      html  css  js  c++  java
  • 【MST】P2323 [HNOI2006]公路修建问题

    Description

    给定 (n) 个点 (m - 1) 条无向边,每条边有两种边权,贵一点的和便宜一点的。要求至少选择 (k) 条贵边使得图联通且花费最大的边权值最小。

    Input

    第一行是三个整数 (n,m,k)

    下面 (m - 1) 行每行描述一条边。

    Output

    输出最小花费与方案。

    Hint

    (1~leq~n~leq~10000,1~leq~m~leq~20000) ,边权 (leq~30000)

    Solution

    两种做法。

    首先题面已经非常明显的提示二分答案,于是我们二分最大花费,然后对两种边分别跑克鲁斯卡尔即可。由于较贵的边有限制,所以我们优先跑较贵的边的克鲁尔卡尔,跑完再跑便宜的边。

    这样做的正确性是因为对于一条合法的两个不同的联通块之间的边,他被枚举到的时候是一定会被选择的。

    注意到克鲁斯卡尔的复杂度为排序 (O(m log m)),主算法 (O(m~alpha(n))),本题不需要排序,但是一共跑了 (O(log c)) 次主算法,所以总体时间复杂度为 (O(m~alpha(n)~log c))

    看起来那个 (O(alpha(n)~ imes~log c)) 非常不优美,直觉告诉我这一部分是可以被去掉的,于是我们考虑优化上述算法。

    注意到我们在上述算法中对边权是否排序是不影响答案的,所以我们不妨对贵的边进行排序。然后我们发现对贵的边跑 MST 的时候等价于选择出至少 (k) 条不同联通块之间的边。于是我们直接贪心的选出 (k) 条边权最小的且能构成合法联通块的边。即对贵的边跑 MST 直到选出 (k) 条边。

    我们发现剩下的边选贵的还是便宜的是无所谓的,于是我们将他们混在一起排序,再对剩下的边跑一遍 MST 即可得答案。

    这样做我们一共进行了 (O(1)) 次克鲁斯卡尔 (O(m~alpha(n))) 的主算法,排序复杂度为 (O(m~log m))。由于 于是总复杂度 (O(m~(log m + alpha(n)))。成功的去掉了乘积

    Code

    (O(m~alpha(n)~log c)):

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    #define ci const int
    #define cl const long long
    
    typedef long long int ll;
    
    namespace IPT {
        const int L = 1000000;
        char buf[L], *front=buf, *end=buf;
        char GetChar() {
            if (front == end) {
                end = buf + fread(front = buf, 1, L, stdin);
                if (front == end) return -1;
            }
            return *(front++);
        }
    }
    
    template <typename T>
    inline void qr(T &x) {
        char ch = IPT::GetChar(), lst = ' ';
        while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
        while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
        if (lst == '-') x = -x;
    }
    
    namespace OPT {
        char buf[120];
    }
    
    template <typename T>
    inline void qw(T x, const char aft, const bool pt) {
        if (x < 0) {x = -x, putchar('-');}
        int top=0;
        do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
        while (top) putchar(OPT::buf[top--]);
        if (pt) putchar(aft);
    }
    
    const int maxn = 10010;
    const int maxm = 20010;
    
    struct Edge {
        int from, to, v, id;
        
        inline void build(int x, int y, int z, int w) {
            from = x; to = y; v = z; id = w;
        }
    
        inline bool operator<(const Edge &_others) const {
            return this->v < _others.v;
        }
    };
    Edge com[maxm], expen[maxm];
    
    int n, m, k, dn;
    std::vector< std::pair<int, int> > MU;
    int ufs[maxn], rk[maxn];
    
    bool check(int, bool);
    int find(ci);
    void  unionn(int, int);
    
    int main() {
        freopen("1.in", "r", stdin);
        qr(n); qr(k); qr(m); dn = n - 1;
        for (int i = 1, a, b, c; i < m; ++i) {
            a = b = c = 0; qr(a); qr(b); qr(c);
            expen[i].build(a, b, c, i);
            c = 0; qr(c);
            com[i].build(a, b, c, i);
        }
        int l = 1, r = 30000, mid, ans = 0; 
        while (l <= r) {
            mid = (l + r) >> 1;
            if (check(mid, false)) ans = mid, r = mid - 1;
            else l = mid + 1;
        }
        qw(ans, '
    ', true); check(ans, true); std::sort(MU.begin(), MU.end());
        for (int i = 0; i < dn; ++i) {
            qw(MU[i].first, ' ', true); qw(MU[i].second, '
    ', true);
        }
        return 0;
    }
    
    bool check(int x, bool rec) {
        for (int i = 1; i <= n; ++i) ufs[i] = i, rk[i] = 1;
        int cnt = 0;
        for (int i = 1; i < m; ++i) if (expen[i].v <= x) {
            int fa = find(expen[i].from), fb = find(expen[i].to);
            if (fa == fb) continue;
            unionn(fa, fb);
            if (rec) MU.push_back(std::make_pair(expen[i].id, 1));
            ++cnt;
        }
        if (cnt < k) return false;
        for (int i = 1; i < m; ++i) if (com[i].v <= x) {
            int fa = find(com[i].from), fb = find(com[i].to);
            if (fa == fb) continue;
            unionn(fa, fb);
            if (rec) MU.push_back(std::make_pair(com[i].id, 2));
            ++cnt;
            if (cnt == dn) return true;
        }
        return cnt == dn;
    }
    
    int find(int x) {return ufs[x] == x ? x : ufs[x] = find(ufs[x]);}
    
    void unionn(int fa, int fb) {
        if (rk[fa] < rk[fb]) ufs[fa] = fb;
        else if (rk[fb] < rk[fa]) ufs[fb] = fa;
        else ufs[fb] = fa, ++rk[fa];
    }
    

    (O(m (log m + alpha(n)))):

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    #define ci const int
    #define cl const long long
    
    typedef long long int ll;
    
    namespace IPT {
    	const int L = 1000000;
    	char buf[L], *front=buf, *end=buf;
    	char GetChar() {
    		if (front == end) {
    			end = buf + fread(front = buf, 1, L, stdin);
    			if (front == end) return -1;
    		}
    		return *(front++);
    	}
    }
    
    template <typename T>
    inline void qr(T &x) {
    	char ch = IPT::GetChar(), lst = ' ';
    	while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
    	while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
    	if (lst == '-') x = -x;
    }
    
    namespace OPT {
    	char buf[120];
    }
    
    template <typename T>
    inline void qw(T x, const char aft, const bool pt) {
    	if (x < 0) {x = -x, putchar('-');}
    	int top=0;
    	do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
    	while (top) putchar(OPT::buf[top--]);
    	if (pt) putchar(aft);
    }
    
    const int maxn = 20010;
    const int maxm = 40010;
    
    struct Edge {
    	int from, to, v, id, tp;
    	
    	inline void build(int x, int y, int z, int w, int u) {
    		from = x; to = y; v = z; id = w; tp = u;
    	}
    
    	inline bool operator<(const Edge &_others) const {
    		return this->v < _others.v;
    	}
    };
    Edge expen[maxn], tmp, edge[maxm];
    std::vector< std::pair<int, int> >ans;
    
    int n, k, m, ecnt, maxans;
    int ufs[maxn], rk[maxn];
    
    int find(int);
    void unionn(int, int);
    void printans();
    
    int main() {
    	freopen("1.in", "r", stdin);
    	qr(n); qr(k); qr(m); int dn = n - 1;
    	for (int i = 1, a, b, c; i < m; ++i) {
    		a = b = c = 0; qr(a); qr(b); qr(c);
    		expen[i].build(a, b, c, i, 1); c = 0; qr(c);
    		edge[++ecnt].build(a, b, c, i, 2);
    	}
    	std::sort(expen + 1, expen + m);
    	int cnt = 0;
    	for (int i = 1; i <= n; ++i) ufs[i] = i, rk[i] = 1;
    	for (int i = 1; i < m; ++i) {
    		int fa = find(expen[i].from), fb = find(expen[i].to);
    		if (fa == fb) {
    			edge[++ecnt] = expen[i];
    		} else {
    			unionn(fa, fb);
    			maxans = expen[i].v;
    			ans.push_back(std::make_pair(expen[i].id, 1));
    			if ((++cnt) == k) break;
    		}
    	} 
    	std::sort(edge + 1, edge + 1 + ecnt);
    	for (int i = 1; cnt !=  dn; ++i) {
    		int fa = find(edge[i].from), fb = find(edge[i].to);
    		if (fa == fb) continue;
    		unionn(fa, fb);
    		maxans = std::max(maxans, edge[i].v);
    		ans.push_back(std::make_pair(edge[i].id, edge[i].tp));
    		++cnt;
    	}
    	printans();
    	return 0;
    }
    
    int find(int x) {return ufs[x] == x ? x : ufs[x] = find(ufs[x]);}
    
    void unionn(int a, int b) {
    	if (rk[a] < rk[b]) ufs[a] = b;
    	else if (rk[a] > rk[b]) ufs[b] = a;
    	else ufs[b] = a, ++rk[b];
    }
    
    void printans() {
    	qw(maxans, '
    ', true);
    	std::sort(ans.begin(), ans.end());
    	for (auto i : ans) {
    		qw(i.first, ' ', true); qw(i.second, '
    ', true);
    	}
    }
    
  • 相关阅读:
    linux常用命令中篇
    htaccess正则规则学习笔记整理
    个性签名
    求函数的单调区间
    函数的奇偶性
    函数的对称性
    函数的周期性
    复合函数
    赋值法
    高中数学中高频变形技巧收录
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/10406883.html
Copyright © 2011-2022 走看看