题目链接:点击打开链接
题意:
给定n个点m条边的无向图
下面n个数表示每个点的权值。
下面m条边 (u,v) dis
对于每条边,这条边的权值为 原图上删除这条边后,有N对点被分离, 则点权为 N*dis
然后:
让边两端任意一个点的点权上 加上这条边的权值
使得:
最大的点权最小。
问:最小的答案。
思路:
1、首先我们计算每条边的边权。
除了桥,边权都是0,因为不会有点被分离。
所以边双连通缩点一下,得到桥,然后计算一下每条边的边权。
2、
二分一下答案。
对于一棵树,先走到叶子节点,然后把每个点的父边的边权加到这个叶子节点上,若不能加就加到父节点上。
写写写。。
#pragma comment(linker, "/STACK:1024000000,1024000000") #include <bits/stdc++.h> template <class T> inline bool rd(T &ret) { char c; int sgn; if (c = getchar(), c == EOF) return 0; while (c != '-' && (c<'0' || c>'9')) c = getchar(); sgn = (c == '-') ? -1 : 1; ret = (c == '-') ? 0 : (c - '0'); while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0'); ret *= sgn; return 1; } template <class T> inline void pt(T x) { if (x <0) { putchar('-'); x = -x; } if (x>9) pt(x / 10); putchar(x % 10 + '0'); } using namespace std; typedef long long ll; #define N 10010 #define M 20010 struct Edge{ int from,to; ll dis; int next; bool cut; }edge[2*M]; int head[N],edgenum; void init(){edgenum = 0; memset(head,-1,sizeof(head));} int Low[N],DFN[N],Stack[N];//Belong数组的值是1~block int Index,top; int Belong[N],block, B_siz[N];//新图的连通块标号(1~block) bool Instack[N]; int bridge; //割桥数量 void addedge(int u,int v, ll d){ Edge E={u,v,d,head[u],0}; edge[edgenum]=E; head[u] = edgenum++; Edge E2={v,u,d,head[v],0};edge[edgenum]=E2;head[v] = edgenum++; } void Tarjan(int u,int pre){ int v; Low[u] = DFN[u] = ++Index; Stack[top++] = u; Instack[u] = true; for(int i = head[u]; ~i ;i = edge[i].next){ v = edge[i].to; if( v == pre )continue; if( !DFN[v] ){ Tarjan(v,u); if(Low[u] > Low[v])Low[u] = Low[v]; if(Low[v] > Low[u]){ bridge++; edge[i].cut = true; edge[i^1].cut = true; } } else if(Instack[v] && Low[u] > DFN[v])Low[u] = DFN[v]; } if(Low[u] == DFN[u]){ block++; B_siz[block] = 0; do{ v = Stack[--top]; Instack[v] = false; Belong[v] = block; B_siz[block] ++; }while( v != u ); } } void work(int l, int r){ memset(DFN,0,sizeof(DFN)); memset(Instack,false,sizeof(Instack)); Index = top = block = bridge = 0; for(int i = l; i <= r; i++)if(!DFN[i])Tarjan(i,i); } vector<int>G[N];//点标从1-block struct node{ int from, to; ll d; int nex; int oldfrom, oldto; void put(){printf("(%d,%d) val:%lld, (%d,%d)\n", from, to, d, oldfrom, oldto);} }e[2*M]; int H[N], em; void add(int u,int v, ll d,int ou, int ov){ node E= {u,v,d,H[u], ou, ov}; e[em]=E; H[u] = em++; node E2={v,u,d,H[v], ov, ou}; e[em]=E2;H[v] = em++; } void init_new(){memset(H, -1, sizeof H); em = 0;} void suodian(){ init_new(); for(int i = 0; i < edgenum; i+=2){ int u = Belong[edge[i].from], v = Belong[edge[i].to]; if(u==v)continue; add(u,v,edge[i].dis, edge[i].from, edge[i].to); } } int siz[N], fa_id[N]; void find_siz(int u, int fa){ siz[u] = B_siz[u]; fa_id[u] = -1; for(int i = H[u]; ~i; i = e[i].nex){ int v = e[i].to; if(v == fa) { fa_id[u] = i; continue; } find_siz(v, u); siz[u] += siz[v]; } } bool vis[N]; void cal_n(int u, int fa,int tree_node){ vis[u] = 1; for(int i = H[u]; ~i; i = e[i].nex){ int v = e[i].to; if(v == fa) { e[i].d *= (ll)siz[u] * (ll)(tree_node-siz[u]); e[i^1].d = e[i].d; continue; } cal_n(v, u, tree_node); } } void work1(){ memset(siz, 0, sizeof siz); for(int i = 1; i <= block; i++) if(siz[i] == 0) find_siz(i, i); memset(vis, 0, sizeof vis); for(int i = 1; i <= block; i++) if(vis[i] == 0) cal_n(i,i,siz[i]); } int n, m; ll dp[N], a[N], l, r, mid; bool dfs(int u, int fa){ vis[u] = 1; for(int i = H[u]; ~i; i = e[i].nex){ int v = e[i].to; if(v == fa) continue; if(false == dfs(v, u))return false; } if(fa_id[u] != -1) { int ou = e[fa_id[u]].oldfrom, ov = e[fa_id[u]].oldto; if(dp[ou] + e[fa_id[u]].d <= mid) dp[ou] += e[fa_id[u]].d; else if(dp[ov] + e[fa_id[u]].d <= mid) dp[ov] += e[fa_id[u]].d; else return false; } return dp[u] <= mid; } bool ok(){ for(int i = 1; i <= n; i++) dp[i] = a[i]; memset(vis, 0, sizeof vis); for(int i = 1; i <= block; i++) if(vis[i] == 0) { if(dfs(i, i) == false) return false; } return true; } void input(){ rd(n); rd(m); l = r = 0; for(int i = 1; i <= n; i++){ rd(a[i]); l = max(l, a[i]);} init(); int u, v; ll d; while(m--){ rd(u); rd(v); rd(d); addedge(u, v, d); r = max(r, d); } r *= (ll)n*(ll)n; r += l+1; } int main(){ int T, Cas = 1; rd(T); while(T--){ input(); work(1, n); suodian(); work1(); ll ans = r; while(l <= r){ mid = (l+r)>>1; if(ok()){ r = mid-1; ans = min(ans, mid); } else l = mid+1; } printf("Case %d: ", Cas++); pt(ans); puts(""); } return 0; }