长路
长路
(path.pas/c/cpp) 1s 128MB
给出一个连通图,通过每条边i都有一个解锁的代价ci,通过每条边之前必须解锁,但是一旦解锁之后就可以随意通过了。主人公从1号点出发,要访问其他所有点至少一次。但主人公还有一个神奇的技能就是,他可以把一条边的解锁代价变成0,但是技能只能用一次。问在这种情况下,最小总代价是多少?有多少种这样的方案?
输入格式
第一行两个正整数 n, m
以下m行,每行一个整数ai,bi,ci 表示ai 和 bi之间有一条解锁代价为ci的边
输出格式
两个空格隔开的整数a,b,a表示最小总代价,b表示方案数
样例输入
4 6
1 2 1
1 3 3
1 4 7
2 3 2
2 4 3
3 4 3
样例输出
3 3
数据范围
30%:1<=N<=10
60%:1<=N<=5,000,1<=M<=10,000
100%:1<=N<=100,000,1<=M<=200,000,0<=ci<=1,000,000,000
————————————————————————————————
这道题很明显删的边就是最小生成树上的最大边 因为题目要求就是求出最小生成树的和
因为你不是最小生成树上的边对答案的贡献最大也就是最小生成树上的最大边
因为你最多就把这条边替换掉而没有其他更优的贡献
所以我们很容易求出答案就是最小生成树的路径和减去最大边权y
知道答案之后我们可以发现我们枚举边 如果他是树上的点的话 他的贡献就是他自己的值
如果不是那么他的贡献就是他的两个端点在树上路径上的max
所以我们就可以用lca维护一波之后求
但是我们发现我们只需要把树上的边权等于y(不存在大于)的点都删掉
然后枚举一波 如果这条边能使联通块的个数-1就可以作为一种方案
因为如果两个点之间的路径等于y那么他必然被删掉 那么两个点就会分属两个联通块
所以这样复杂度就更低了而且也好写了
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=100007,M=250007; int n,m,mx,ans,ansh,tot,h; int cnt,first[N],sum,f[N],start[N]; struct node{int to,next,from,w;}e[M],q[M]; bool cmp(node a,node b){return a.w<b.w;} void ins1(int a,int b,int w){cnt++; e[cnt].to=b; e[cnt].w=w; e[cnt].from=a; e[cnt].next=first[a];} void ins2(int a,int b,int w){sum++; q[sum].to=b; q[sum].w=w; q[sum].from=a; q[sum].next=start[a];} int find(int x){return f[x]==x?x:f[x]=find(f[x]);} void clear(){for(int i=1;i<=n;i++) f[i]=i;} int main() { freopen("path.in","r",stdin); freopen("path.out","w",stdout); int x,y,w; scanf("%d %d",&n,&m); clear(); for(int i=1;i<=m;i++) scanf("%d %d %d",&x,&y,&w),ins1(x,y,w); sort(e+1,e+1+m,cmp); for(int i=1;i<=m;i++){ int p=find(e[i].from),q=find(e[i].to); if(p==q) continue; f[q]=p; tot++; ins2(e[i].from,e[i].to,e[i].w); if(e[i].w>mx) mx=e[i].w,h=i; if(tot==n-1) break; ans+=e[i].w; } clear(); sort(q+1,q+1+sum,cmp); for(int i=1;i<=sum&&q[i].w<mx;i++){ int x=find(q[i].from),y=find(q[i].to); f[x]=y; } for(int i=h;i<=m;i++){ int x=find(e[i].from),y=find(e[i].to); if(x!=y) ansh++; }printf("%d %d\n",ans,ansh); return 0; }