题解【[AHOI2013]连通图】

\[\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} \]

你可能感兴趣的:(题解【[AHOI2013]连通图】)