题目链接:点击打开链接
题意:给你一张图, n个点, m条边, 求一个最小生成树, 然后Q个询问, 每个询问要求改变一条边, 然后求现在的最小生成树。
思路:最暴力的做法是, 对于每个询问, 改变这个边(一定是增大)之后再做一遍最小生成树。 复杂度O(qm),显然会超时。 那么我们可以发现, 对于每个询问推一步的结果如下:
1. 改变的这条边不在最小生成树中, 那么答案就是最小生成树。
2.改变的这条边在最小生成树中, 那么我们要在最小生成树之外找一条权值最小的边,使得树联通。 由于O(qn)的复杂度是可以接受的, 因此我们可以处理出每个点到另一个点所形成的子树中任意一个点的最小权值(不在原树中) , 那么我们可以用dfs做到这一点。
细节参见代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<string> #include<vector> #include<stack> #include<bitset> #include<cstdlib> #include<cmath> #include<set> #include<list> #include<deque> #include<map> #include<queue> #define Max(a,b) ((a)>(b)?(a):(b)) #define Min(a,b) ((a)<(b)?(a):(b)) using namespace std; typedef long long ll; typedef long double ld; const ld eps = 1e-9, PI = 3.1415926535897932384626433832795; const int mod = 1000000000 + 7; const int INF = int(1e9) + 100; // & 0x7FFFFFFF const int seed = 131; const ll INF64 = ll(1e18); const int maxn = 3000 + 10; int T,n,m,tree[maxn][maxn],tot,p[maxn],mp[maxn][maxn],d[maxn][maxn],vis[maxn][maxn]; int _find(int x) { return p[x] == x ? x : p[x] = _find(p[x]); } struct node { int a, b, c; node(int a=0, int b=0, int c=0):a(a), b(b), c(c) {} bool operator < (const node& rhs) const { return c < rhs.c; } }a[maxn*maxn]; vector<int> g[maxn]; void init() { for(int i=0;i<n;i++) { for(int j=0;j<n;j++) mp[i][j] = d[i][j] = INF, vis[i][j] = 1; g[i].clear(); p[i] = i; } } int dfs(int root, int u, int fa) { int len = g[u].size(); int ans = INF; for(int i=0;i<len;i++) { int v = g[u][i]; if(v != fa) { int cur = dfs(root, v, u); ans = min(ans, cur); d[u][v] = d[v][u] = min(d[u][v], cur); } } if(root != fa) ans = min(ans, mp[root][u]); return ans; } void solve() { sort(a, a+m); tot = 0; memset(tree, 0, sizeof(tree)); for(int i=0;i<m;i++) { int x = _find(a[i].a), y = _find(a[i].b); if(x != y) { p[x] = y; tot += a[i].c; vis[a[i].a][a[i].b] = vis[a[i].b][a[i].a] = 0; g[a[i].a].push_back(a[i].b); g[a[i].b].push_back(a[i].a); } } for(int i=0;i<n;i++) { dfs(i, i, -1); } } int q, x, y, c; int main() { while(~scanf("%d%d",&n,&m)) { if(!n && !m) break; init(); for(int i=0;i<m;i++) { scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c); mp[a[i].a][a[i].b] = mp[a[i].b][a[i].a] = a[i].c; } solve(); scanf("%d",&q); double ans = 0; for(int i=0;i<q;i++) { scanf("%d%d%d",&x,&y,&c); if(vis[x][y]) ans += tot; else { ans += tot*1.0 - mp[x][y] + min(d[x][y], c); } } printf("%.4f\n",ans/(double)q); } return 0; }