这是一道相对裸的并查集。
因为洛谷题面本身就很简短,在这里就不再做题意简述,下面直接来讲做法。
题目让我们求至少删掉多少条边能使得编号小于等于 \(k\) 的点都不在环上,而在并查集中删除并不好操作,于是我们考虑转化。
因为题目只是要求回答个数而非方案,所以我们可以把删点操作转化为计数,具体实现如下。
考虑到最初那个连通块在删去所有边之后,剩下的必然只有编号 \(x\) 满足大约 \(k\) 的点,于是我们在最初建图的时候可以先只把满足要求的点并在一起。
那么剩下的必然都是编号小于等于 \(k\) 的点,考虑如何选择能让题面中所述的删去点的次数更少。
在最坏的情况下,我们把每个点都删除一次。如果我们能利用题目中所给出的边把剩下的点并在一起,就能减少我们的删除操作次数从而减小答案。
实际上面所述的内容用并查集实现很简单,于是下面的代码部分不做过多解释。
#include <iostream> #include <stdio.h> #include <math.h> #include <algorithm> #include <string.h> #define Heriko return #define Deltana 0 #define Romanno 1 #define S signed #define LL long long #define R register #define I inline #define CI const int #define mst(a, b) memset(a, b, sizeof(a)) #define ON std::ios::sync_with_stdio(false);cin.tie(0) using namespace std; template<typename J> I void fr(J &x) { short f(1); char c=getchar(); x=0; while(c<'0' or c>'9') { if(c=='-') f=-1; c=getchar(); } while (c>='0' and c<='9') { x=(x<<3)+(x<<1)+c-'0'; c=getchar(); } x*=f; } template<typename J> I void fw(J x,bool k) { if(x<0) putchar('-'),x=-x; static short stak[35]; short top(0); do { stak[top++]=x%10; x/=10; } while(x); while(top) putchar(stak[--top]+'0'); if(k) putchar('\n'); else putchar(' '); } CI MXX=8e5+5; int n,m,k,f[MXX],ans; struct line { int x,y; } r[MXX]; I int Find(const int &x) { if(x!=f[x]) f[x]=Find(f[x]); Heriko f[x]; } S main() { fr(n),fr(m),fr(k); for(R int i(1);i<=n;++i) f[i]=i; for(R int i(1);i<=m;++i) fr(r[i].x),fr(r[i].y); for(R int i(1);i<=m;++i) if(r[i].x>k and r[i].y>k) f[Find(r[i].x)]=Find(r[i].y); for(R int i(1);i<=m;++i) { int fx=Find(r[i].x),fy=Find(r[i].y); if(r[i].x<=k or r[i].y<=k) { if(fx!=fy) f[fx]=fy; else ++ans; } } fw(ans,1); Heriko Deltana; }
希望大家能有所收获。
审核的管理员辛苦了!