题解:P4350 [CERC2015] Export Estimate

题目传送门

题目大意

给出一个 \(n\) 个点 \(m\) 条边的无向图,每一条边有边权。每次会给出一个数 \(k\),先把所有边权小于 \(k\) 的都删去。再重复进行一个操作,依照编号从小到大进行操作,如果第 \(i\) 个点度为 \(0\) 则删去这个点,如果第 \(i\) 个点的度为 \(2\) 则删去这个点以及与它相连的两条边,再让与 \(i\) 点相连的两个点之间连一条边。求给定每个 \(k\) 后,最终图中会有几个点和几条边。每次操作并不会影响下一个 \(k\) 的结果,也让就是说,每次操作都是独立的。

题目分析

首先想到删边不好维护。就应该想到把操作离线下来,按 \(k\) 从大到小排序,这样删边就转换成了加边,很好维护。我们考虑这两种操作对图的影响,注意到第一和第二种操作不会影响其他点的度数,而第一种操作会让总点数减一,而第二种操作会让总点数和总边数都减一。所以我们可以想到用并查集维护加边的同时维护一下度数,每次加边前判断一下度数是否为 \(0\)\(2\),如果是就要将度数为 \(0\) 或度数为 \(2\) 的总数减一。再将这两个点的度数都加一后再判断一下是否度数为 \(2\) 即可。

如果当前加的边数为 \(m\), 度数为 \(0\) 的点数为 \(num0\),度数为 \(2\) 的点数为 \(num2\),那么答案就是 \(n-num0-num2\)\(m-num2\)

但是我们会发现一个问题,如果出现简单环,即图中每个点都是度数为 \(2\) 的点,那么依据题目给定的操作最终会变成一个自环,而自环在题中是不会被删掉的,所以我们要判断一下当前图中是否有简单环,判断方法也很简单,如果图中度为 \(2\) 的点数等于总点数,那么就说明这个联通块是一个简单环。

如果当前图中有 \(c\) 个简单环,那么答案就是 \(n-num0-num2+c\)\(m-num2+c\)

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1010101;
ll n,m,fa[N],deg[N],deg2[N],cir[N],q,siz[N],d;
struct edge{ll u,v,w;}s[N],p[N],ans[N];
bool cmp(edge a,edge b){return a.w>b.w;}
ll find(ll x){
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++)fa[i]=i,siz[i]=1;
    for(int i=1;i<=m;i++)cin>>s[i].u>>s[i].v>>s[i].w;
    sort(s+1,s+m+1,cmp);
    cin>>q;
    for(int i=1;i<=q;i++)cin>>d,p[i]={i,0,d};
    sort(p+1,p+q+1,cmp);
    ll num0=n,num2=0,c=0;
    for(int i=1,j=0;i<=q;i++){
        while(j<m&&s[j+1].w>=p[i].w){
            j++;
            ll x=find(s[j].u),y=find(s[j].v);
            if(!deg[s[j].u])num0--;
            if(!deg[s[j].v])num0--;
            if(deg[s[j].u]==2)num2--,deg2[x]--;
            if(deg[s[j].v]==2)num2--,deg2[y]--;
            deg[s[j].u]++,deg[s[j].v]++;
            c-=cir[x],cir[x]=0;
            if(x!=y)fa[y]=x,siz[x]+=siz[y],deg2[x]+=deg2[y],c-=cir[y];
            if(deg[s[j].u]==2)num2++,deg2[x]++;
            if(deg[s[j].v]==2)num2++,deg2[x]++;
            if(deg2[x]==siz[x])c++,cir[x]=1;
        }
        ans[p[i].u]={n-num0-num2+c,j-num2+c,0};
    }
    for(int i=1;i<=q;i++)cout<<ans[i].u<<" "<<ans[i].v<<'\n';
    return 0;
}
posted @ 2025-09-07 21:27  一班的hoko  阅读(9)  评论(0)    收藏  举报