题意:
给定一张无向连通图,每次询问时保留边权 (geq t) 的边,并对这张图进行如下操作:
- 从小到大枚举每个点 (i) ,若其度数为 (0) 直接删去。
- 若其度数为 (2) ,则找出连向 (i) 的点 (u,v)(可能相同),并删去点 (i) 以及连向 (u,v) 的两条边,再添加 (u,v) 一条边。((u,v) 相同时形成一个自环)
( ext{Sulotion})
如果将询问按 (t) 从大到小排序,那每次需要的图就由之前的加上一些边得到,边权递减。
先考虑每个操作流程对点与边的影响。
设一个度数为 (k) 的点称为 (k) 度点。则可以发现在第一、第二个流程中受影响的点仅有 (0) 度点与 (2) 度点。
每个 (0) 度点会将点的总量减 (1) ,而每个 (2) 度点会将点总数减 (1) ,且将边总数减 (1) 。而每个对 (2) 度点的操作不会影响其他点的度数。
所以只需统计出 (0) 度点与 (2) 度点的个数 (sum_0,sum_2) ,以及出现过得边的个数 (edg) 。那总点数就是 (n-sum_0-sum_2), 总边数就是 (edge-sum_2) 。由于是动态加边,可以用并查集维护连通块内 (2) 度点与 (0) 度点个数。
但事实上并非如此,在某个连同图是一个单纯的环时会出现问题。
因为按照上文说法这个环会没有点剩余,而环上所有点都为 (2) 度点。但它最终剩余的是一个自环,点数与变数要加上 (1) 。设纯环个数为 (k),则最终点与边数为 (n-sum_0,-sum_2+k) 与 (edge-sum_2+k) 。
而由于纯环上所有点都为二度点,并查集同时维护点的个数后是容易判定的。
时间复杂度 (O(nalpha(n)))
代码:(个人认为实现方法还算简单)
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int n,m,s,x,y,v,ax,ay;
int s_nd2,s_nd0,s_cyc;
struct node{
int x,y,w;
bool operator <(const node &x)const{return w>x.w;}
}e[N],q[N],ans[N];
int f[N],sz[N],sz2[N],deg[N];bool cyc[N];char ch;
inline int getf(int x){while(x^f[x])x=f[x]=f[f[x]];return x;}
inline void read(int &x){
x=0;ch=getchar();while(ch<48)ch=getchar();
while(ch>47)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
}
void write(int x){if(x>9)write(x/10);putchar(48+x%10);}
main(){
read(n),read(m);register int i,j=0,s,t;
for(i=1;i<=m;++i)read(x),read(y),read(v),e[i]=(node){x,y,v};
read(s);for(i=1;i<=s;++i)read(v),q[i]=(node){i,0,v};
sort(e+1,e+m+1);sort(q+1,q+s+1);
s_nd0=n;
for(i=1;i<=n;++i)sz[i]=1,f[i]=i;
for(i=1;i<=s;++i){
while(e[j+1].w>=q[i].w&&j<m){
++j;
x=e[j].x,y=e[j].y;ax=getf(x),ay=getf(y);
if(!deg[x])--s_nd0;if(deg[x]==2)--s_nd2,--sz2[ax];
if(!deg[y])--s_nd0;if(deg[y]==2)--s_nd2,--sz2[ay];
++deg[x];++deg[y];
s_cyc-=cyc[ax];cyc[ax]=0;
if(ax^ay)f[ay]=ax,s_cyc-=cyc[ay],sz[ax]+=sz[ay],sz2[ax]+=sz2[ay];
if(deg[x]==2)++s_nd2,++sz2[ax];
if(deg[y]==2)++s_nd2,++sz2[ax];
if(sz2[ax]==sz[ax])cyc[ax]=1,++s_cyc;
}
ans[q[i].x]=(node){n-s_nd0-s_nd2+s_cyc,j-s_nd2+s_cyc};
}
for(i=1;i<=s;++i)write(ans[i].x),putchar(' '),write(ans[i].y),putchar('
');
}