\[\texttt{Description} \]
给一个 \(n\) 个点,\(m\) 条边的无向图。
共 \(k\) 次询问,每次询问给出一个边集。问:删去边集中的边后,改图是否为连通图。询问互相独立。
允许离线。
\[\texttt{Solution} \]
-
线段树分治好题。
-
任意删边显然是不好做的,我们还是考虑把删边转化为加边。
-
我们将询问的序列看成一个时间轴,我们会发现:每条边会在若干个时间区间内出现。
-
具体地,我们考虑任意一条边 \(x\) ,设 \(x\) 边被删除 \(w\) 次,删除的位置分别是 \(P_1,P_2,...,P_w\) ,那么我们可以视为:\(x\) 边在时间区间 \([1,P_1-1],[P_1+1,P_2-1],...,[P_w+1,k]\) 中出现。
-
我们发现,像这种 " 操作在若干个时间区间内有效 " 的问题,恰恰是线段树分治易于解决的。
-
离线处理出每条边被删除的位置,解决这个可以用 \(m\) 个
STL vector
来解决。 -
对于每一条边,将其挂在 " 该边出现的时间区间对应的线段树节点 " 的
STL vector
上。 -
最后,我们跑一边整棵线段树,进行相应的操作。操作的过程我们可以使用 " 可撤销并查集 " 维护。
-
再在并查集上维护一下 " 当前连通块大小(\(size\))" ,对于任意加进去的边,若加边之后的连通块大小为 \(n\) ,则说明当前图为连通图(否则还有 \(n-size\) 个节点不在当前连通块中)。
-
为了不破坏并查集形态,优化选择使用 " 按秩合并 " 。
-
\(\mathcal{O(kc \log k\log n)}\) ,评测链接 。
\[\texttt{Code} \]
#include
#include
#include
#include
#include
#define RI register int
using namespace std;
namespace IO
{
static char buf[1<<20],*fs,*ft;
inline char gc()
{
if(fs==ft)
{
ft=(fs=buf)+fread(buf,1,1<<20,stdin);
if(fs==ft)return EOF;
}
return *fs++;
}
#define gc() getchar()
inline int read()
{
int x=0,f=1;char s=gc();
while(s<'0'||s>'9'){if(s=='-')f=-f;s=gc();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=gc();}
return x*f;
}
}using IO::read;
const int N=100100,M=200100;
int n,m;
int a[M],b[M];
int k;
vectorpos[M];
struct SegmentTree{
int l,r;
vectore;
}t[N*4];
void build(int p,int l,int r)
{
t[p].l=l,t[p].r=r;
if(l==r)return;
int mid=(l+r)/2;
build(p*2,l,mid),build(p*2+1,mid+1,r);
}
void insert(int p,int l,int r,int x)
{
if(l<=t[p].l&&t[p].r<=r)
{
t[p].e.push_back(x);
return;
}
int mid=(t[p].l+t[p].r)/2;
if(l<=mid)
insert(p*2,l,r,x);
if(mids;
void solve(int x,bool flag)
{
int ori=s.size();
for(RI i=0;i<(int)t[x].e.size();i++)
{
int u=a[t[x].e[i]],v=b[t[x].e[i]];
int p=get(u),q=get(v);
if(p==q)continue;
if(d[p]ori)
{
Node res=s.top();s.pop();
d[fa[res.x]]-=res.y,size[fa[res.x]]-=res.z,fa[res.x]=res.x;
}
}
int main()
{
n=read(),m=read();
for(RI i=1;i<=m;i++)
a[i]=read(),b[i]=read();
k=read();
for(RI i=1;i<=m;i++)
pos[i].push_back(0);
for(RI i=1;i<=k;i++)
for(RI c=read();c;c--)
pos[read()].push_back(i);
for(RI i=1;i<=m;i++)
pos[i].push_back(k+1);
build(1,1,k);
for(RI i=1;i<=m;i++)
for(RI j=1;j<(int)pos[i].size();j++)
if(pos[i][j-1]+1<=pos[i][j]-1)
insert(1,pos[i][j-1]+1,pos[i][j]-1,i);
for(RI i=1;i<=n;i++)
fa[i]=i,size[i]=1,d[i]=1;
solve(1,false);
return 0;
}
\[\texttt{Thanks} \ \texttt{for} \ \texttt{watching} \]