要把n个点用n-1条边连接起来,还要使总边权和最小,那么很明显是求最小生成树,用克鲁斯卡尔算法可解。
但是这题有一个限制条件:
一定要用n-2条王牌电缆+1条李牌电缆组成最小生成树。
那我们就先用n-1条王牌电缆建立最小生成树。
然后枚举所有李牌的边,如果一条李牌边是从x到y费用z,因为要使结果是一颗树,所以要把新生成的环上边权最大的边删去(即在x到y的路径上删去一条最大的王牌的边)
这个边显然可以用倍增求lca的方法找出.
但是
我忘了倍增怎么写……
所以我用了一个树链剖分来求(大材小用)……
ps:有一种情况是王牌的边建完最小生成树之后只有n-2条边,所以需要进行特判。
pss:我tm的改了两个钟就是因为一个初始化没打和一个树链剖分的sb细节错误???
psss: 280+行的程序也是够了,有毒.
#include
#include
#include
#include
#include
#include
#define maxn 300005
#define maxm 600005
#define INF 0x7fffffff;
using namespace std;
struct gg{
int x,y,w;
int next;
int wei;
}edge[maxm];
int ls[maxn];
int edge_m;
gg edge1[maxm];
int edge_m1;
bool cmp(gg a,gg b)
{
return a.wint max,wei;
};
struct arr{
int x,y;
ar max;
}f[maxm*10];
int a[maxn];
int n,W,L;
void swap1(int &x,int &y)
{
int z;
z=x; x=y; y=z;
return;
}
void add1(int x,int y,int w,int wei)
{
edge1[++edge_m1]=(gg){x,y,w,0,wei};
}
void add(int x,int y,int w,int wei)
{
edge[++edge_m]=(gg){x,y,w,ls[x],wei},ls[x]=edge_m;
edge[++edge_m]=(gg){y,x,w,ls[y],wei},ls[y]=edge_m;
}
int insert(int r,int x,int y,int add,int wei)//线段树修改操作。
{
if (f[r].max.maxif ((f[r].x==x)&&(f[r].y==y))
{
return 0;
}
int mid=(f[r].x+f[r].y)/2;
if (y<=mid) insert(r*2,x,y,add,wei);
else if (x>mid) insert(r*2+1,x,y,add,wei);
else insert(r*2,x,mid,add,wei),insert(r*2+1,mid+1,y,add,wei);
}
int maketree(int r,int x,int y)//线段树建树
{
f[r].x=x; f[r].y=y;
f[r].max.max=0;
if (x==y) return 0;
int mid=(x+y)/2;
maketree(r*2,x,mid);
maketree(r*2+1,mid+1,y);
}
int siz[maxn],dep[maxn],top[maxn],fa[maxn],son[maxn],w[maxn],ed[maxn];
void dfs1(int x,int r)//树链dfs1
{
fa[x]=r;
siz[x]=1;
dep[x]=dep[r]+1;
int mx=0;
for (int i=ls[x];i;i=edge[i].next)
{
if (edge[i].y==r) continue;
dfs1(edge[i].y,x);
ed[edge[i].y]=i;
siz[x]+=siz[edge[i].y];
if (mxy])
{
mx=siz[edge[i].y];
son[x]=edge[i].y;
}
}
return;
}
int num=0;
void dfs2(int x,int st)//树链dfs2
{
num++;
w[x]=num; top[x]=st;
if (son[x]!=0) dfs2(son[x],st);
for (int i=ls[x];i;i=edge[i].next)
{
if ((edge[i].y!=fa[x])&&(edge[i].y!=son[x]))
dfs2(edge[i].y,edge[i].y);
}
return;
}
ar findmax(int r,int x,int y)//线段树
{
if ((f[r].x==x)&&(f[r].y==y)) return f[r].max;
int mid=(f[r].x+f[r].y)/2;
if (y<=mid) return findmax(r*2,x,y);
else if (x>mid) return findmax(r*2+1,x,y);
ar k1=findmax(r*2,x,mid),k2=findmax(r*2+1,mid+1,y);
if (k1.max>=k2.max)
return k1;
else
return k2;
}
ar solvemax(int x,int y)//树链
{
ar mx=(ar){0,0};
int f1=top[x];
int f2=top[y];
while (f1!=f2)
{
if (dep[f1]x,y); swap1(f1,f2);};
ar k1=findmax(1,w[f1],w[x]);
if (k1.max>mx.max) mx=k1;
x=fa[f1]; f1=top[x];
}
if (x==y) return mx;
if (dep[x]>dep[y]) swap1(x,y);
x=son[x];
ar k1=findmax(1,w[x],w[y]);
if (k1.max>mx.max) mx=k1;
return mx;
}
int father[maxn];
int found(int x)
{
if (father[x]==x) return x;
else{
father[x]=found(father[x]);
return father[x];
}
}
void merge(int x,int y)
{
int x1,y1;
x1=found(x);
y1=found(y);
father[x1]=y1;
}
int ans=0,mxx;
void tree()
{
sort(edge1+1,edge1+1+W,cmp);
for (int i=1;i<=n;i++) father[i]=i;
int i=1; num=0;
while ((num1)&&(i<=W))
{
if (found(edge1[i].x)!=found(edge1[i].y))
{
ans+=edge1[i].w;
//printf("%d %d\n",ans,i);
merge(edge1[i].x,edge1[i].y);
add(edge1[i].x,edge1[i].y,edge1[i].w,edge1[i].wei);
num++;
}
i++;
}
//printf("\n");
mxx=ans;
}
void init()
{
scanf("%d%d%d\n",&n,&W,&L);
for (int i=1;i<=W;i++)
{
int x,y,w;
scanf("%d%d%d\n",&x,&y,&w);
add1(x,y,w,i);
}
}
long long mi=INF;
int sp()
{
int num=found(1),flag=0,u,v;
for (int i=2;i<=n;i++)
{
found(i);
if (father[i]!=num)
{
u=num;
v=father[i];
flag=1;
break;
}
}
for (int i=1;i<=n;i++)
{
found(i);
//printf("%d\n",father[i]);
}
if (flag)
{
int anss=0;
for (int i=1;i<=L;i++)
{
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
if (((father[x]==v)&&(father[y]==u))||((father[y]==v)&&(father[x]==u)))
if (wprintf("%d\n",ans+mi);
for (int i=1;i<=edge_m;i+=2)
{
printf("%d\n",edge[i].wei);
}
printf("%d",anss);
}
return flag;
}
int main()
{
freopen("telephone.in","r",stdin);
freopen("telephone.out","w",stdout);
init();
tree();
if (sp())
{
fclose(stdin);
fclose(stdout);
return 0;
}
maketree(1,1,maxm*2);
dfs1(1,0);
dfs2(1,1);
for (int i=1;i<=n;i++)
{
insert(1,w[i],w[i],edge[ed[i]].w,edge[ed[i]].wei);
}
int wei=0,wei2=0;
int ans=INF;
for (int i=1;i<=L;i++)
{
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
ar k=solvemax(x,y);
if (ans>mxx-k.max+w) ans=mxx-k.max+w,wei=k.wei,wei2=i;
}
printf("%d\n",ans);
for (int i=1;i<=edge_m;i+=2)
{
if (edge[i].wei!=wei) printf("%d\n",edge[i].wei);
}
printf("%d",wei2);
fclose(stdin);
fclose(stdout);
return 0;
}