CodeForces 125E MST Company

时间:2024-04-22 11:04:41
E. MST Company
time limit per test 8 seconds
memory limit per test 256 megabytes
input standard input
output standard output

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

代码基本靠抄,自己吃枣药丸。

传说这题做法主要有两种:

一:

  先做出不含1点的最小生成树。

  如果将连接到1的边加进生成树会形成环的话,就在环中找到最长的一条边删掉。

  ↑计算加某条边会使答案增加的量,然后从连接到1的边里选出增量最小的边进行上述操作,重复k次得到最终结果。

  ↑写了好久好久都写不出,怒砸键盘,换第二种写法。结果第二天看到隔壁yhx大神分分钟按上述算法切题……

  害怕。

  附传送门:http://blog.****.net/sdfzyhx/article/details/53500851

  还有自己写了半天调不对的代码,姑且先存着:

  

 #include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define LL unsigned long long
using namespace std;
const int mxn=;
int read(){
int x=,f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>='' && ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
struct edge{int x,y;int v;int id;}e[mxn*];
int cmp(const edge a,const edge b){return a.v<b.v;}
struct sdd{int v,nxt,dis,id;}eg[mxn<<];
int hd[mxn],egct=;
void add_edge(int u,int v,int dis,int id){
eg[++egct].v=v;eg[egct].nxt=hd[u];eg[egct].dis=dis;eg[egct].id=id;hd[u]=egct;return;
}
int n,m,k;
int ans=;
int st[mxn],top;
int fir[mxn*],mct=;//存所有与1相连的边
//
int fa[mxn];
int find(int x){if(fa[x]==x)return x;return fa[x]=find(fa[x]);}
int add[mxn];
bool del[mxn];
bool intree[mxn];
inline void init(int x){for(register int i=;i<=x;i++)fa[i]=i;}//并查集初始化
void PD(){
int cnt=;
for(int i=;i<=n;i++){if(find(i)==i)cnt++;}
if(cnt>k){printf("-1\n");exit();}//联通块数多于可加边数,无解
return;
}
int mx[mxn],m_id[mxn];
void DFS(int u,int f){
for(int i=hd[u];i;i=eg[i].nxt){
int v=eg[i].v;
if(v==f || !intree[eg[i].id])continue;
if(u==){mx[v]=1e9;m_id[v]=;}
bool flag=;
if(eg[i].dis<mx[v]){
mx[v]=eg[i].dis;
m_id[v]=eg[i].id;
flag=;
}
if(mx[v]>mx[u]){
mx[v]=mx[u];
m_id[v]=m_id[u];
flag=;
}
if(flag)DFS(v,u);
}
return;
}
int belone[mxn];
void solve(){
top=;
int i,j,u,v;
int cnt=;
//
sort(e+,e+m+,cmp);
init(n);
for(i=;i<=m;i++){
if(e[i].x== || e[i].y==)continue;
u=find(e[i].x);v=find(e[i].y);
if(u!=v){
fa[u]=v;
ans+=e[i].v;
intree[e[i].id]=;//记录是否在树中
add_edge(e[i].x,e[i].y,e[i].v,e[i].id);
add_edge(e[i].y,e[i].x,e[i].v,e[i].id);
cnt++;
}
if(cnt==n-)break;//除1以外都连通时,退出
}
//kruskal处理出除点1以外的生成树
PD();
//
init(n);
for(i=;i<=n;i++){belone[i]=find(i);}
for(i=;i<=m;i++)//找出待加的1边
if(e[i].x== || e[i].y==){
if(e[i].y==)swap(e[i].x,e[i].y);
fir[++mct]=i;
}
memset(mx,0x3f,sizeof mx);
for(i=;i<=mct;i++){
if(e[fir[i]].v<mx[belone[e[fir[i]].y]]){
mx[belone[e[fir[i]].y]]=e[fir[i]].v;
m_id[belone[i]]=fir[i];
}
}
cnt=;
for(i=;i<=n;i++){//加入和点1相连的边使图连通
if(belone[i]!=i)continue;
intree[e[m_id[i]].id]=;
cnt++;
ans+=e[m_id[i]].v;
add_edge(e[m_id[i]].x,e[m_id[i]].y,e[m_id[i]].v,e[m_id[i]].id);
add_edge(e[m_id[i]].y,e[m_id[i]].x,e[m_id[i]].v,e[m_id[i]].id);
}
for(i=cnt+;i<=k;i++){
DFS(,);
int tmp1=1e9,tmp2,tmp3;
for(j=;j<=mct;j++){//尝试替换1边
int to=e[fir[j]].y;
if(e[fir[j]].v-mx[to]<tmp1){
tmp1=e[fir[j]].v+mx[to];
tmp2=fir[j];
tmp3=m_id[to];
}
ans+=tmp1;
intree[e[tmp2].id]=;//加入1边
intree[e[tmp3].id]=;//删除一条边
}
}
return;
}
int main()
{
int i,j;
n=read();m=read();k=read();
for(i=;i<=m;i++){
e[i].x=read();e[i].y=read();
e[i].v=read();e[i].id=i;
fa[find(e[i].x)]=fa[find(e[i].y)];
}
for(i=;i<n;i++)if(find(i)!=find(i+)){
printf("-1\n");return ;
}//无法连通,无解
solve();
printf("%d\n",ans);
return ;
}

二:

  加边的增量具有单调性。二分可能的增量,以此为基准将与1相连的边排序并加入生成树,看何时能正好加入k条,就是答案了。

  

 #include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define LL unsigned long long
using namespace std;
const double eps=1e-;
const int mxn=;
int read(){
int x=,f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>='' && ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
struct edge{
int x,y;
int v;
int id;
}e[mxn*];
double mid;
int cmp(const edge a,const edge b){
return (double)(a.x==)*mid+(double)a.v < (double)(b.x==)*mid+(double)b.v;
}
int n,m,k;
int tot=;
//
int fa[mxn];
int find(int x){
if(fa[x]==x)return fa[x];
return fa[x]=find(fa[x]);
}
int ans[mxn],mct=;
void solve(bool flag){
for(int i=;i<=n;i++)fa[i]=i;
sort(e+,e+m+,cmp);
tot=;mct=;
for(int i=;i<=m;i++){
int u=find(e[i].x),v=find(e[i].y);
if(u!=v && (tot+(e[i].x==)<=k || flag)){
fa[u]=v;
ans[++mct]=e[i].id;
if(e[i].x==)tot++;
}
}
}
int main()
{
n=read();m=read();k=read();
int i,j;
int dg1=;
for(i=;i<=m;i++){
e[i].x=read();e[i].y=read();e[i].v=read();e[i].id=i;
if(e[i].x>e[i].y)swap(e[i].x,e[i].y);
if(e[i].x==)dg1++;
}
if(dg1<k || (n> && k==)){printf("-1\n");return ;}//不能满足k要求
mid=;
solve();
if(mct<n-){printf("-1\n");return ;}//不能生成树
double l=-1e5,r=1e5;
while(l+eps<r && tot!=k){
mid=(l+r)/;
solve();
if(tot<k)r=mid;
else l=mid;
}
if(tot!=k)mid=(l+r)/;
solve();
printf("%d\n",mct);
for(i=;i<=mct;i++)printf("%d ",ans[i]);
return ;
}