专栏文章

题解:P4350 [CERC2015] Export Estimate

P4350题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mio0pq6v
此快照首次捕获于
2025/12/02 11:28
3 个月前
此快照最后确认于
2025/12/02 11:28
3 个月前
查看原文

题目传送门

题目大意

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

题目分析

首先想到删边不好维护。就应该想到把操作离线下来,按 kk 从大到小排序,这样删边就转换成了加边,很好维护。我们考虑这两种操作对图的影响,注意到第一和第二种操作不会影响其他点的度数,而第一种操作会让总点数减一,而第二种操作会让总点数和总边数都减一。所以我们可以想到用并查集维护加边的同时维护一下度数,每次加边前判断一下度数是否为 0022,如果是就要将度数为 00 或度数为 22 的总数减一。再将这两个点的度数都加一后再判断一下是否度数为 22 即可。
如果当前加的边数为 mm, 度数为 00 的点数为 num0num0,度数为 22 的点数为 num2num2,那么答案就是 nnum0num2n-num0-num2mnum2m-num2
但是我们会发现一个问题,如果出现简单环,即图中每个点都是度数为 22 的点,那么依据题目给定的操作最终会变成一个自环,而自环在题中是不会被删掉的,所以我们要判断一下当前图中是否有简单环,判断方法也很简单,如果图中度为 22 的点数等于总点数,那么就说明这个联通块是一个简单环。
如果当前图中有 cc 个简单环,那么答案就是 nnum0num2+cn-num0-num2+cmnum2+cm-num2+c

代码

CPP
#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;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...