fzoj4493 糟糕的网络_题解

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;
}
上一篇:暴力+组合数学+预处理+双指针——cf 1371E1+E2


下一篇:P3567 [POI2014]KUR-Couriers 主席树