fzoj4493 糟糕的网络_题解
20pts
m = n − 1 m=n-1 m=n−1,是一棵树的情况,直接求出整棵树的边权和,加减上差值即可。
50pts
n ≤ 1500 n\le 1500 n≤1500,留了一些分给写的好的暴力加上一些正解的处理,纯暴力 O ( m 2 log m ) O(m ^{2}\log m) O(m2logm) 是没有分的。
100pts
首先我们可以先求出原图的最小生成树,然后我们可以分为四种情况处理每个询问。
-
树边改小
答案即为原最小生成树减去改变的差值。
-
非树边改大
答案即为原最小生成树。
-
非树边改小
设原最小生成树的总权值 a n s 1 ans _{1} ans1,一定要包含这条非树边的最小生成树的总权值 a n s 2 ans _{2} ans2,答案即为 min ( a n s 1 , a n s 2 ) \min(ans _{1},ans _{2}) min(ans1,ans2)。关键在于怎么求 a n s 2 ans _{2} ans2。我们采用与次小生成树的同一套路,用这条非树边来换掉到 LCA 的路径上最大的一条边即可。
-
树边改大
只有两种情况,第一种就是依然经过这条树边,答案即为原最小生成树加上改变的差值。第二种就是不经过这条树边,用另一条非树边替代它(就是将这条树边断掉后的两个连通块之间的边权最小的非树边)。考虑枚举每一条非树边。它所能代替的树边为它端点在生成树上的路径上的边。处理这种用非树边 ( u , v ) (u,v) (u,v) 更新树上 u → LCA ( u , v ) u\to \text{LCA}(u,v) u→LCA(u,v) 和 v → LCA ( u , v ) v\to \text{LCA}(u,v) v→LCA(u,v) 路径的边的问题有一个经典做法,我们要用最小的非树边,那我们就先将其从小到大排序,这样就保证每个树上的点第一次被更新的答案就是最优的,并且每个点只会更新唯一一次(因为并查集在更新后会把节点的代表元指向其 LCA)。接着利用并查集往上跳,同时更新答案即可。最终答案就是第一种情况和第二种情况取最小值。
最后把答案加起来即可。时间复杂度 O ( m log m ) O(m\log m) O(mlogm)。期望得分 100 100 100 分。
#include<bits/stdc++.h>
#define INF 1e9
using namespace std;
typedef long long ll;
const int N=3010;
ll n,m,q,fa[N];
ll head[N],cnt,f[N][31];
ll pre[N],dep[N];
ll t[N],maxx[N][31];
bool mapp[N][N],vis[N*N];
int pos[N*N];
struct edge{
ll x,y,z; int id;
} s[N*N];
struct node{
int nxt,to;
ll num;
}e[N<<1];
inline void add(int from,int to,int num){
e[++cnt].nxt=head[from],e[cnt].to=to,e[cnt].num=num,head[from]=cnt;
}
inline bool cmp(edge a,edge b) { return a.z<b.z; }
int getfa(int x) { return fa[x]==x?x:fa[x]=getfa(fa[x]); }
void dfs(int u,int faa){
dep[u]=dep[faa]+1,f[u][0]=faa;
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].to;
if(v==faa||f[v][0]) continue;
maxx[v][0]=e[i].num;
pre[v]=e[i].num;
dfs(v,u);
}
}
ll find(int x,int y) {
if(dep[x]<dep[y]) swap(x,y);
ll l=0,r=0;
for(int i=30;i>=0;i--)
if(dep[f[x][i]]>=dep[y])
l=max(l,maxx[x][i]),x=f[x][i];
if(x==y) return l;
for(int i=30;i>=0;i--)
if(f[x][i]!=f[y][i]){
l=max(l,maxx[x][i]); r=max(r,maxx[y][i]);
x=f[x][i]; y=f[y][i];
}
l=max(l,maxx[x][0]);
r=max(r,maxx[y][0]);
return max(l,r);
}
int main(){
// freopen("terrible.in","r",stdin);
// freopen("terrible.out","w",stdout);
scanf("%lld %lld",&n,&m);
ll ans=0;
for(int i=1;i<=m;i++)
scanf("%lld%lld%lld",&s[i].x,&s[i].y,&s[i].z), s[i].id=i;
sort(s+1,s+m+1,cmp);
for(int i=1;i<=m;i++) pos[s[i].id]=i;
for(int i=1;i<=n;i++) fa[i]=i;
int x,y;
for(int i=1;i<=m;i++){
x=getfa(s[i].x),y=getfa(s[i].y);
if(x!=y){
fa[x]=y;
add(s[i].x,s[i].y,s[i].z),add(s[i].y,s[i].x,s[i].z);
ans+=s[i].z;
mapp[s[i].x][s[i].y]=mapp[s[i].y][s[i].x]=vis[i]=1;
}
}
dfs(1,0);
for(int j=1;j<=30;j++)
for(int i=1;i<=n;i++){
f[i][j]=f[f[i][j-1]][j-1];
maxx[i][j]=max(maxx[i][j-1],maxx[f[i][j-1]][j-1]);
}
for(int i=1;i<=n;i++) fa[i]=i,t[i]=INF;
for(int i=1;i<=m;i++)
if(!vis[i]){
int x=getfa(s[i].x);
int y=getfa(s[i].y);
while(x!=y){
if(dep[x]<dep[y]) swap(x,y);
t[x]=ans+s[i].z-pre[x];
fa[x]=f[x][0];
x=getfa(x);
}
}
ll sum=0; int u,v;
for(int i=1;i<=m;i++){
u=s[pos[i]].x,v=s[pos[i]].y;
ll z=s[pos[i]].z;
ll w;
scanf("%lld",&w);
if(dep[u]<dep[v]) swap(u,v);
if((!mapp[u][v])&&(!mapp[v][u])) {
if(w>=z) sum+=ans;
else{
ll num=find(u,v);
sum+=min(ans,ans+w-num);
}
}
else {
if(w>=z) sum+=min(ans+w-z,t[u]);
else sum+=(ans-z+w);
}
}
printf("%lld\n",sum);
return 0;
}