zoukankan      html  css  js  c++  java
  • 题解【[AHOI2013]连通图】

    [ exttt{Description} ]

    给一个 (n) 个点,(m) 条边的无向图。

    (k) 次询问,每次询问给出一个边集。问:删去边集中的边后,改图是否为连通图。询问互相独立。

    允许离线。

    [ exttt{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 klog n)})评测链接

    [ exttt{Code} ]

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<stack>
    
    #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;
    
    vector<int>pos[M];
    
    struct SegmentTree{
    	int l,r;
    	vector<int>e;
    }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(mid<r)
    		insert(p*2+1,l,r,x); 
    }
    
    int fa[N],size[N],d[N]; 
    
    int get(int x)
    {
    	while(fa[x]!=x)
    		x=fa[x];
    	return x;
    }
    
    struct Node{
    	int x,y,z;
    	Node(int v1,int v2,int v3):x(v1),y(v2),z(v3){}
    };
    
    stack<Node>s;
    
    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]<d[q])
    			swap(p,q);
    
    		s.push((Node){q,d[p]==d[q],size[q]});
    
    		fa[q]=p,d[p]+=d[p]==d[q],size[p]+=size[q];
    
    		if(size[p]==n)
    			flag=true;
    	}
    
    	if(t[x].l==t[x].r)
    		puts(flag?"Connected":"Disconnected");
    	else
    		solve(x*2,flag),solve(x*2+1,flag);
    
    	while(s.size()>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;
    }
    

    [ exttt{Thanks} exttt{for} exttt{watching} ]

  • 相关阅读:
    Encryption (hard) CodeForces
    cf 1163D Mysterious Code (字符串, dp)
    AC日记——大整数的因子 openjudge 1.6 13
    AC日记——计算2的N次方 openjudge 1.6 12
    Ac日记——大整数减法 openjudge 1.6 11
    AC日记——大整数加法 openjudge 1.6 10
    AC日记——组合数问题 落谷 P2822 noip2016day2T1
    AC日记——向量点积计算 openjudge 1.6 09
    AC日记——石头剪刀布 openjudge 1.6 08
    AC日记——有趣的跳跃 openjudge 1.6 07
  • 原文地址:https://www.cnblogs.com/cjtcalc/p/12454145.html
Copyright © 2011-2022 走看看