CF125E. MST Company详细解答

time limit per test: 8 seconds

memory limit per test: 256 megabytes

The MST (Meaningless State Team) company won another tender for an important state reform in Berland.

There are n cities in Berland, some pairs of the cities are connected by roads. Each road has its price. One can move along any road in any direction. The MST team should carry out the repair works on some set of roads such that one can get from any city to any other one moving only along the repaired roads. Moreover, this set should contain exactly k capital roads (that is, the roads that start or finish in the capital). The number of the capital is 1.

As the budget has already been approved, the MST Company will profit by finding the set with minimum lengths of roads.

Input

The first input line contains three integers n, m, k (1 ≤ n ≤ 5000;0 ≤ m ≤ 105;0 ≤ k < 5000), where n is the number of cities in the country, m is the number of roads in the country, k is the number of capital roads in the required set. Then m lines enumerate the roads in question. Each road is specified by three numbers ai, bi, wi (1 ≤ ai, bi ≤ n; 1 ≤ w ≤ 105), where ai, bi are the numbers of cities linked by a road and wi is its length.

Between each pair of cities no more than one road exists. There are no roads that start and finish in one city. The capital's number is 1.

Output

In the first line print the number of roads in the required set. The second line should contain the numbers of roads included in the sought set. If the sought set does not exist, print -1.

Examples

input

4 5 2
1 2 1
2 3 1
3 4 1
1 3 3
1 4 2

output

3
1 5 2 

题意翻译

求一种特殊的最小生成树。给定一个有nn个节点和mm条边的图,找出一个生成树满足从根节点11直接连向其余节点的边要恰好是kk条,在此条件下生成树的权值和最小。

首先这道题运用的是WQS二分算法,WQS算法对于这道题的适用要求是画一个平面直角坐标系,横轴代表连接1的边的条数,纵轴代表对应连接1的边数的情况下的最小生成树权值和,这个图像能形成一个向下的凸包,有关详细原理想了解的可以看关于WQS二分算法以及其一个细节证明 - Creeper_LKF - 博客园,这里我提供为什么这道题的是向下凸包的证明,首先假设连接1的边都是在最小生成树中不必要的,也就是说可有可无,那么就是每条边都可以有它的对应的不连接1的替换边,有些是比该边权值小的,有些是大的。假设在不考虑连接1的边数量的情况下,最小生成树中连接1的边数为m,现在要强行把连接1的边数设定为m-1,那么就是在m的最小生成树中找一条边和不连接1的边替换,首先替换后的权值肯定是变大的,否则就有更小的生成树了,那么我们选择替换后权值增量最小的来替换,便得到了m-1对应的最小生成树,同理,要m-2的最小生成树,那么还是要选择增量最小的替换,此时的最小增量肯定要大于或等于从m替换到m-1的最小增量,那么画图像就会是如下所示:

 一个凸包的大概形状就出来了,那么以此类推,以及右边的也是同样原理,最终图像肯定是形成凸包,而无法替换的边(必然会出现的,因为没有必然不会出现的,根据kruskal原理,只要你在二分的时候把边排的够前它就能取到)则完全不会影响结果,至于可以一条边多种替换的,当然是选权值增量最小的来替换,总之就会是凸包就对了。

会是凸包是一回事,题目还要我们在符合连接1的边数为k的情况下输出是哪些边参与构成,要是不存在或者说刚好二分出k条边的情况那不用说,直接输出就是了,而重点就在于一种情况:当给连接1的边加权值为t的时候得到的最小生成树中连接边数是大于k的,但是当加t+1的时候又是小于k的,这才是问题的关键,下面我将提供并详细证明这种情况的解法。

首先明确加t和加t+1前后所有边顺序的改变处是什么,就是+t的时候某条连接1的边的边权比它前面一条边(只讨论简单的相邻情况,复杂的也同样可以推出)的边权小1,但是+t+1之后二者相等,顺序就颠倒过来了(相等的话连接1的边优先,这个关系到二分中情况等于最优的时候要把mid分配给l还是r,这里我就设定相等的话连接1的边优先比较方便处理),而影响结果的就是这些交换顺序的相邻边对,毕竟只有顺序变了才可能发生变化。

那么为了讨论的严密性,结合kruskal的原理,我们首先看到第一对发生顺序交换的边对{a,b}

注意前面的顺序不变的意义就是两种情况到了{a,b}边对之前连接的边集合是完全一样的。其中a为不连接1的边,b为连接1的边,那么我们首先分情况讨论影响:

①原来的a,b都参与或者都不参与集合,那么显然交换后也都参与或者都不参与集合;

②原来的a不参与集合但b参与集合,那交换后就肯定还是b参与集合但a不参与集合;

③原来的a参与集合但b不参与集合,这个就有点意思了,首先讨论交换后b还是不参与集合,那么a肯定还是参与集合,情况还是不变;再讨论交换后b参与集合了,那么此时此刻!无疑原来的b无法参与就是因为a在前面挡住了(因为{a,b}前面的都是一样的),也就是说b的职能和a是完全一样的(连接当前相同的两个联通块并使之成为一个),那么交换后a也就肯定不参与集合了,但是有趣的是,这样{a,b}边对交换后也并不影响后边任何边的连接情况(因为还有这句“Between each pair of cities no more than one road exists”没有重边的意思,其实有重边也不用多说,先选到的后面就都选不到了,不用管的)。

综合上面所有情况的讨论,得出“第一个边对的交换不影响后面任何边连接情况”的结论,这就意味着所有边对的交换都不影响其他边的连接,也就是说你要选择这里面的任何一个边对替换还是不替换,都能形成一颗最小生成树!!!并且我们发现这些替换导致的权值变换都是一样的(为1)!!!那么在二分结束后直接拿那个+t+1的情况再跑一次kruskal,并且在连接1的边数达到k之后就保持后面的边对都不再进行替换,由此就得出了连接边数为k的最小生成树的边集了!!!最友好的代码附上:

#include<iostream>
#include<algorithm>
#include<map>
#include<cmath>
#include<cstring>
#include<iomanip>
#include<numeric>
#include<stack>
#include<queue>
#include<set>
#include<string>
#include<vector>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
int n,m,k;
vector<int> fa;
vector<int> rk;
struct edge{
    int u,v,w,id;
    bool operator<(const edge &a)const
    {
        return w<a.w||w==a.w&&(u==1||v==1);
    }
};
vector<edge> g;
struct kruskal{
	int n,m;
	ll sum;
    void makeSet()
    {
        fa.resize(n+1);
		rk.resize(n+1,1);
		iota(fa.begin(),fa.end(),0);
    }
	kruskal(int nn,int mm):n(nn),m(mm),sum(0)
	{
        makeSet();
		g.resize(m);
	}
	void read()
	{
		for(int i=0;i<m;i++)
		{
            g[i].id=i+1;
			cin>>g[i].u>>g[i].v>>g[i].w;
		}
	}
	int find(int a)
	{
		return fa[a]==a?a:fa[a]=find(fa[a]);
	}
	bool same(int a,int b)
	{
		return find(a)==find(b);
	}
	void unite(int a,int b)
	{
		if(same(a,b))return;
        a=find(a);
        b=find(b);
		if(rk[a]>rk[b])
		{
			fa[b]=a;
			rk[a]+=rk[b];
		}
		else
		{
			fa[a]=b;
			rk[b]+=rk[a];
		}
	}
	int solve(int mid,bool flag=0)
	{
        int cnt=0;
        makeSet();
        for(auto &e:g)if(e.u==1||e.v==1)e.w+=mid;
		sort(g.begin(),g.end());
		for(auto e:g)
		{
			if(same(e.u,e.v)||flag&&cnt>=k&&(e.u==1||e.v==1))continue;
			unite(e.u,e.v);
            if(e.u==1||e.v==1)cnt++;
            if(flag)
            {
                cout<<e.id<<" ";
            }
		}
        for(auto &e:g)if(e.u==1||e.v==1)e.w-=mid;
        return cnt;
	}
};

int main()
{
    ios_base::sync_with_stdio(0),cin.tie(0);
    cin>>n>>m>>k;
    kruskal t(n,m);
    t.read();
    int l=-1e5-1,r=1e5+1,cnt;
    while(l+1<r)
    {
        int mid=(l+r)/2,tmp=t.solve(mid);
        if(tmp>=k)
        {
            cnt=tmp;
            l=mid;
        }
        else r=mid;
    }
    if(r==1e5+1&&cnt>k||l==-1e5-1)
    {
        cout<<-1;
        return 0;
    }
    cout<<n-1<<"\n";
    t.solve(l,1);
	return 0;
}

还有就是补充一下另一种特地挑选出k条边的方法,就是给每条连接到1的边都加上一个和自身有关的一个很小值,比如第i条边就加上1e-7*i,那么在正常情况下的排序是不会受到影响的,只有到最后那个情况(上面所说的相差为1的多对边对)的时候这时就不再是都相差1了,而都是各不相同的,因此把l和r夹到1e-8的情况就能挑选出特定的几个交换边对,最终在合法的情况下一定能挑选出k的最小生成树,当然这种方法的合理性也是以上面我的证明作为前提的。

最后想说:创作不易,给个赞,给一个对于证明有强迫症的人一点动力(bushi !毕竟像这种题目在网上可以说完全翻不出一点优质的解说,很多大概都是知其然不知其所以然并且不想追究吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值