Description
在小头在还在为老师的难题绞尽脑汁时,幸福的小明开始了最爱玩的经营类游戏——模拟城市4。在游戏中有n个城市,用编号1..n表示,他希望建造若干条道路来改善交通(初始时不存在任何道路)。
小明拟定了m条道路的计划,每条道路连接城市x和y,路是双向的,道路的安全程度为p,道路价值为c。小明是这样开始游戏的:他每次选择一条道路建造,如果建完后城市之间存在一个环,则删去环中安全值最小的道路(如果有多条路安全值一样,则删去最晚建造的那条)
小明希望能够确定一个建造顺序,使得最后剩下的道路的价值和最大。
虽然这个问题要比小头遇到的问题简单不少,但是考虑到小明的智商……所以还需要聪明的你出马
Input
第一行二个正整数n,m
接下来2-m+1行
第i行四个正整数,x、y、p、c依次表示第(i-1)条道路连接的城市、安全程度和道路价值。
数据保证两个城市之间最多只有一条道路,且没有自环。
Output
两行
第一行一个整数表示最大的价值和
第二行是一个1到m的排列,即表示建造道路的顺序,两个数之间用一个空格表示,行末无空格。若有多组解输出字典序最小的那组。
Sample Input
4 4
1 2 1 1
2 4 1 2
1 3 1 2
3 4 3 1
Sample Output
5
2 3 1 4
Data Constraint
Hint
【数据规模】
50%的数据n,m≤8
100%的数据n,m≤300; 0≤p,c≤100000
Solution
题目要求第一问的结果要在安全值最大的情况下,价值和最大,那么我们可以先以p为第一关键字,c为第二关键字从大到小排序,做生成树,那么生成树的价值之和即为第一问答案。而生成树则是第二问加边减边后的最后形成的树。因为生成树上的所有边,最终都要被保存,所以我们需要寻找被删的边中,可能影响必存边上的边。那么我们从小到大枚举被删边,显然,被删边会与生成树上的某些边形成一个环。被删边可能会影响环上其它与其p相等的边,那么,被删边和与其p相等的必存边的先后读入顺序是会影响答案的。显然,我们必须使必存边放在被删边的前面,如果被删边的序号小于必须边的序号,我们又要求字典序最小,那么此被删边定然紧跟在必存边之后。即使一个环中可能有多条p相等的边,但答案要求字典序最小,所以我们只需要把被删边从属在与其p相等的序号最大的必存边上。于是,我们就得到了一些从属关系,简单的打个标记,对于那些不从属与其它边的边(包括被从属的边),排序,并把紧跟的从属边补上。最终就得到了第二问的答案序列。
我们先排序后做一遍最大生成树,它可以直接帮我们解决在树中可能出现的环中删去了一个安全值最小的边,但是考虑到还有相等的情况,我们知道在相等的安全值的边中,树边一定比被删边前,所以我们将相同安全值的被删边挂在树边中编号最大的上面,最后最后那些树边和没有被挂的被删边可以任意顺序,而被删且被挂的边则要在线记录在一个数组中,待输出时与当前输出的编号进行比较,然后输出较小的。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 610
using namespace std;
int n,m,x[N],y[N],d[N],p[N],v[N],id[N],fa[N],c[N],bz[N],map[N][N],q[N],ql,qr;
int t[N],nx[N],l[N],s[N],sa[N],f[N],id2[N],id3[N],ans=0,rt,md;
int t2[N],n2[N],l2[N];
int cmp(int x,int y){return x<y;}
void add(int x,int y,int p,int d){
t[++t[0]]=y;nx[t[0]]=l[x];l[x]=t[0];s[t[0]]=p;id2[t[0]]=d;
}
void add2(int x,int y){
t2[++t2[0]]=y;n2[t2[0]]=l2[x];l2[x]=t2[0];
}
void qs(int l,int r){
int i=l,j=r,m1=p[(l+r)>>1],m2=v[(l+r)>>1],m3=id[(l+r)>>1];
while(i<=j){
while(p[i]>m1||(p[i]==m1&&v[i]>m2)||(p[i]==m1&&v[i]==m2&&id[i]<m3)) i++;
while(p[j]<m1||(p[j]==m1&&v[j]<m2)||(p[j]==m1&&v[j]==m2&&id[j]>m3)) j--;
if(i<=j){
swap(x[i],x[j]);swap(y[i],y[j]);swap(p[i],p[j]);
swap(v[i],v[j]);swap(c[id[i]],c[id[j]]);swap(id[i],id[j]);
i++;j--;
}
}
if(l<j) qs(l,j);if(i<r) qs(i,r);
}
int get(int x){
if(f[x]==x) return x;
return f[x]=get(f[x]);
}
void dg(int x,int y,int z,int di){
fa[x]=y;d[x]=d[y]+1;sa[x]=z;id3[x]=di;
for(int k=l[x];k;k=nx[k]){
if(t[k]!=y) dg(t[k],x,s[k],id2[k]);
}
}
void LCA(int x,int y,int z){
if(d[x]<d[y]) swap(x,y);
md=0;
while(d[x]>d[y]){
if(sa[x]==z) md=max(md,id3[x]);
x=fa[x];
}
if(x==y) return;
while(x!=y){
if(sa[x]==z) md=max(md,id3[x]);
if(sa[y]==z) md=max(md,id3[y]);
x=fa[x];y=fa[y];
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&x[i],&y[i],&p[i],&v[i]);
id[i]=i;c[i]=i;
}
qs(1,m);
for(int i=1;i<=max(n,m);i++) f[i]=i,bz[i]=1;
for(int i=1;i<=m;i++){
int fx=get(x[i]),fy=get(y[i]);
if(fx!=fy){
add(x[i],y[i],p[i],id[i]);add(y[i],x[i],p[i],id[i]);
f[fx]=fy;f[0]++;
ans+=v[i];
rt=x[i];bz[id[i]]=2;
}
if(f[0]==n-1) break;
}
dg(rt,0,0,0);
printf("%d\n",ans);
for(int i=1;i<=m;i++){
if(bz[i]!=2){
LCA(x[c[i]],y[c[i]],p[c[i]]);
if(md){
bz[i]=0;
map[md][0]++;map[md][map[md][0]]=i;
}
}
}
ql=1;qr=0;
for(int i=1;i<=m;i++){
if(bz[i]){
while(ql<=qr&&q[ql]<i){
printf("%d ",q[ql]);
q[ql++]=0;
}
for(int j=1;j<=map[i][0];j++) q[++qr]=map[i][j];
if(qr-ql+1==0) printf("%d ",i);
else{
printf("%d ",i);
sort(q+1,q+1+qr,cmp);
while(ql<=qr&&q[ql]<i){
printf("%d ",q[ql]);
q[ql++]=0;
}
}
}
}
while(ql<=qr) printf("%d ",q[ql++]);
return 0;
}
作者:zsjzliziyang
QQ:1634151125
转载及修改请注明
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/93533416