【KM】BZOJ1937 [Shoi2004]Mst 最小生成树

这道题拖了好久因为懒,结果1A了,惊讶∑( 口 ||

【题目大意】

给定一张n个顶点m条边的有权无向图。现要修改各边边权,使得给出n-1条边是这张图的最小生成树,代价为变化量的绝对值。求最小代价之和。

【思路】

思路有点神,并不是我这种蒟蒻能够想到的XD

显然由贪心,树边必定变成wi-di,非树边必定变成wi+di (di≥0)

为了满足Mst的性质,考察一条非树边j,它加最小生成树后,必定构成一个环。对于环上的每一条树边i,有wi-di≤wj+dj,即di+dj≥wi-wj。这非常类似于KM的形式x[i]+y[i]≥wt[i][j]。

那么我们就如下操作:对于最小生成树,先预处理出所有节点的深度。对于一条非树边E(u,v),求出lca(u,v)。对于u->lca,lca->v上的每一条树边,由树边向非树边连一条(w树边-w非树边)的边。然后跑KM。

注意这个值可能小于0,由于变化量的绝对值之和必定大于0,我们就取为0即可。

【错误点】

lca和KM都写挂了一发orz

LCA的时候忘记了swim后,若u==v,返回u。

KM取slack的时候是取min的。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<vector>
  6 #include<cmath>
  7 using namespace std;
  8 const int MAXN=55;
  9 const int MAXM=800+50;
 10 const int INF=0x7fffffff;
 11 struct Edge
 12 {
 13     int u,v,w,t;
 14 }edge[MAXM];
 15 vector<int> E[MAXN];
 16 int val[MAXM][MAXM];
 17 int dep[MAXN],anc[MAXN][20],faedge[MAXN];
 18 int cnt,m,n;
 19
 20 /**build tree**/
 21 void addedge(int u,int v,int w)
 22 {
 23     edge[++cnt]=(Edge){u,v,w,0};
 24     E[u].push_back(cnt);
 25     E[v].push_back(cnt);
 26 }
 27
 28 void dfs(int rt,int fa,int id)
 29 {
 30     dep[rt]=dep[fa]+1;
 31     anc[rt][0]=fa;
 32     faedge[rt]=id;
 33     for (int i=0;i<E[rt].size();i++)
 34     {
 35         int now=E[rt][i];
 36         if (edge[now].t && edge[now].u!=fa && edge[now].v!=fa)
 37         {
 38             if (edge[now].u==rt) dfs(edge[now].v,rt,now);
 39                 else dfs(edge[now].u,rt,now);
 40         }
 41     }
 42 }
 43
 44 /*lca*/
 45 int getanc()
 46 {
 47     for (int i=1;i<20;i++)
 48         for (int j=1;j<=n;j++)
 49             anc[j][i]=anc[anc[j][i-1]][i-1];
 50 }
 51
 52 int swim(int u,int H)
 53 {
 54     int i=0;
 55     while (H>0)
 56     {
 57         if (H&1) u=u[anc][i];
 58         H>>=1;
 59         i++;
 60     }
 61     return u;
 62 }
 63
 64 int LCA(int u,int v)
 65 {
 66     if (dep[u]<dep[v]) swap(u,v);
 67     if (dep[u]!=dep[v]) u=swim(u,dep[u]-dep[v]);
 68     if (u==v) return u;//不要忘了这句语句
 69     for (int i=19;i>=0;i--)
 70     {
 71         if (anc[u][i]!=anc[v][i])
 72         {
 73             u=anc[u][i];
 74             v=anc[v][i];
 75         }
 76     }
 77     return anc[u][0];
 78 }
 79
 80
 81 /*KM*/
 82
 83 void Addedge(int u,int v,int w)
 84 {
 85     val[u][v]=max(w,0);//由于两条边的变化量的绝对值之和不可能为负数,则必定设为0 ☆☆☆☆☆☆
 86 }
 87
 88 void build(int a,int b,int id)
 89 {
 90     if (dep[a]<dep[b]) swap(a,b);
 91     while (a!=b)
 92     {
 93         Addedge(faedge[a],id,edge[faedge[a]].w-edge[id].w);
 94         a=anc[a][0];
 95     }
 96 }
 97
 98 int fx[MAXM],fy[MAXM],visx[MAXM],visy[MAXM],slack[MAXM],lk[MAXM];
 99
100 int Hungary_dfs(int u)
101 {
102     visx[u]=1;
103     for (int i=1;i<=m;i++)
104     {
105         int wt=fx[u]+fy[i]-val[u][i];
106         if (!visy[i] && wt==0)
107         {
108             visy[i]=1;
109             if (lk[i]==-1 || Hungary_dfs(lk[i]))
110             {
111                 lk[i]=u;
112                 return 1;
113             }
114         }
115         else slack[i]=min(slack[i],wt);//注意这里是取较小值不是较大
116     }
117     return 0;
118 }
119
120 void KM()
121 {
122     memset(lk,-1,sizeof(lk));
123     for (int i=1;i<=m;i++)
124     {
125         fx[i]=-INF;
126         fy[i]=0;
127         for (int j=1;j<=m;j++) fx[i]=max(fx[i],val[i][j]);
128     }
129     for (int i=1;i<=m;i++)
130     {
131         memset(visx,0,sizeof(visx));
132         memset(visy,0,sizeof(visy));
133         memset(slack,0x3f,sizeof(slack));
134         while (!Hungary_dfs(i))
135         {
136             int delta=INF;
137             for (int j=1;j<=m;j++)
138                 if (!visy[j]) delta=min(delta,slack[j]);
139             for (int j=1;j<=m;j++)
140             {
141                 if (visx[j])
142                 {
143                     fx[j]-=delta;
144                     visx[j]=0;
145                 }
146                 if (visy[j])
147                 {
148                     fy[j]+=delta;
149                     visy[j]=0;
150                 }
151             }
152         }
153     }
154     int ans=0;
155     for (int i=1;i<=m;i++) ans+=(fx[i]+fy[i]);
156     printf("%d",ans);
157 }
158
159 /**main part**/
160 void init()
161 {
162     cnt=0;
163     scanf("%d%d",&n,&m);
164     for (int i=1;i<=m;i++)
165     {
166         int u,v,w;
167         scanf("%d%d%d",&u,&v,&w);
168         addedge(u,v,w);
169     }
170
171     for (int i=1;i<=(n-1);i++)
172     {
173         int x,y;
174         scanf("%d%d",&x,&y);
175         for (int j=0;j<E[x].size();j++)
176         {
177             int id=E[x][j];
178             if ((edge[id].u==x && edge[id].v==y)||(edge[id].v==x && edge[id].u==y))
179             {
180                 edge[id].t=1;
181                 break;
182             }
183         }
184     }
185     dfs(1,0,0);//建立以1为根的树,方便后续lca操作。注意仅有树边加入,非树边不加入
186 }
187
188 void solve()
189 {
190     memset(val,0,sizeof(val));
191     getanc();
192     for (int i=1;i<=m;i++)
193     {
194         if (!edge[i].t)
195         {
196             int lca=LCA(edge[i].u,edge[i].v);
197             build(edge[i].u,lca,i);
198             build(edge[i].v,lca,i);
199         }
200     }
201     KM();
202 }
203
204 int main()
205 {
206     init();
207     solve();
208     return 0;
209 }
时间: 2024-10-27 07:33:18

【KM】BZOJ1937 [Shoi2004]Mst 最小生成树的相关文章

[BZOJ1937][SHOI2004]Mst最小生成树(KM算法,最大费用流)

1937: [Shoi2004]Mst 最小生成树 Time Limit: 3 Sec  Memory Limit: 64 MBSubmit: 802  Solved: 344[Submit][Status][Discuss] Description Input 第 一行为N.M,其中 表示顶点的数目, 表示边的数目.顶点的编号为1.2.3.…….N-1.N.接下来的M行,每行三个整数Ui,Vi,Wi,表示顶点Ui与Vi之间有一条边,其权值为 Wi.所有的边在输入中会且仅会出现一次.再接着N-1

BZOJ1937 [Shoi2004]Mst 最小生成树

首先由贪心的想法知道,树边只减不加,非树边只加不减,令$w_i$表示i号边原来的边权,$d_i$表示i号边的改变量 对于一条非树边$j$连接着两个点$x$.$y$,则对于$xy$这条路径上的所有树边$i$,都要满足:$w_i - d_i \le w_j + d_j$ 移项可得$w_i -w_j \le d_i + d_j$ 于是我们发现$d[]$就是KM算法里的顶标了,直接跑最大匹配即可 1 /***************************************************

【BZOJ1937】[Shoi2004]Mst 最小生成树 KM算法(线性规划)

[BZOJ1937][Shoi2004]Mst 最小生成树 Description Input 第一行为N.M,其中 表示顶点的数目, 表示边的数目.顶点的编号为1.2.3.…….N-1.N.接下来的M行,每行三个整数Ui,Vi,Wi,表示顶点Ui与Vi之间有一条边,其权值为Wi.所有的边在输入中会且仅会出现一次.再接着N-1行,每行两个整数Xi.Yi,表示顶点Xi与Yi之间的边是T的一条边. Output 输出最小权值 Sample Input 6 9 1 2 2 1 3 2 2 3 3 3

BZOJ 1937: [Shoi2004]Mst 最小生成树 [二分图最大权匹配]

传送门 题意: 给一张无向图和一棵生成树,改变一些边的权值使生成树为最小生成树,代价为改变权值和的绝对值,求最小代价 线性规划的形式: $Min\quad \sum\limits_{i=1}^{m} \delta_i$ $Sat\quad $非树边边权$\ge$生成树上路径任何一条边的边权 $i$非树边$j$树边 $w_i+\delta_i \ge w_j-\delta_j$ 然后可以转化成二分图最小顶标和来求解 这里需要求二分图最大权非完美匹配,我的做法是遇到$d[t] < 0$就退出,反正这

POJ1258 Agri-Net MST最小生成树题解

搭建一个最小代价的网络,最原始的最小生成树的应用. 这里使用Union find和Kruskal算法求解. 注意: 1 给出的数据是原始的矩阵图,可是须要转化为边表示的图,方便运用Kruskal,由于须要sort 2 降低边.一个矩阵最多须要(N*N-N)>>1条边,有人讨论本题是否有向,那是无意义的.由于本题的最小生成树和方向无关. 3 使用Union find是为了推断是否有环.比原始推断快非常多. #include <stdio.h> #include <stdlib.

UVA 1151 Buy or Build (MST最小生成树,kruscal,变形)

题意:要使n个点之间能够互通,要使两点直接互通需要耗费它们之间的欧几里得距离的平方的权(注意不需要开方的),这说明每两个点都可以使其互通.接着又q个套餐可以选,一旦选了这些套餐,他们所包含的点自动就连起来了,所需要做的就是连上还未通的即可,q<=8.可以多买.求最小生成树所需的代价. 思路:与普通求MST不同的就是多了套餐,而且还可以多买.每个套餐有买或不买两种可能,那么有28种可能,即256种. 如果不买套餐,至少需要求1次MST是确定的,这个复杂度已经是O(n*n)了.还得考虑哪些餐套可以搭

CF 160D Edges in MST 最小生成树的性质,寻桥,缩点,批量处理 难度:3

http://codeforces.com/problemset/problem/160/D 这道题要求哪条边存在于某个最小生成树中,哪条边不存在于最小生成树中,哪条边绝对存在于最小生成树中 明显桥边一定存在于所有最小生成树中,然而怎么处理存在某个最小生成树的边呢? 借助kruskal算法的性质,由小到大,每次处理同一权值的边,如果边连接的点已经联通就不要管,否则那些处理的边一定存在于某最小生成树上 批量处理的思想很巧妙 #include <cstdio> #include <vecto

poj 1679 The Unique MST 最小生成树

点击打开链接 http://poj.org/problem?id=1679 题意:给一个无向图,问最小生成树是否唯一,如果唯一就输出最小生成树的所有边的权值的和,如果不唯一,那么就输出Not Unique! 思路:在用prime算法求最小生成树的过程中,在找到权值最小的一个节点之后,先判断一下,这个节点的权值是否可以由好几条路径求得,并且权值都等于当前权值,如果是的话,那么最小生成树就不唯一.那么如何判断呢?可以用一个flag数组标记,0代表这个点得到当前权值的路径唯一,1代表不唯一,初始化全为

【bzoj2238】Mst 最小生成树+树链剖分+线段树

题目描述 给出一个N个点M条边的无向带权图,以及Q个询问,每次询问在图中删掉一条边后图的最小生成树.(各询问间独立,每次询问不对之后的询问产生影响,即被删掉的边在下一条询问中依然存在) 输入 第一行两个正整数N,M(N<=50000,M<=100000)表示原图的顶点数和边数. 下面M行,每行三个整数X,Y,W描述了图的一条边(X,Y),其边权为W(W<=10000).保证两点之间至多只有一条边. 接着一行一个正整数Q,表示询问数.(1<=Q<=100000) 下面Q行,每行