zoukankan      html  css  js  c++  java
  • CF891C-Envy【可撤销并查集】

    正题

    题目链接:https://www.luogu.com.cn/problem/CF891C


    题目大意

    (n)个点(m)条边的一张无向联通图,每次询问一个边集能否同时出现在同一棵最小生成树上。

    (1leq n,m,q,w_i,sum kleq 5 imes 10^5)


    解题思路

    考虑(Kruskal)的做法,每次找一条最小的边然后判断是否能够加入最小生成树。

    考虑到边权相同的边可以任意排序,我们把它称之为同一层的边,并且无论任意排序最后产生的图的连通情况都不会改变。

    我们把一个询问的边按照边分层,那么每层的边都合法这个询问就合法。

    然后对于一个询问同一层的边,直接离线,然后把前面的层都加入之后,再把所有这些询问边加入判断是否合法即可。因为前面的层不能重新插,所以做完要撤销回去。

    时间复杂度:(O(nlog n))(全部同级的话)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<stack>
    using namespace std;
    const int N=5e5+10;
    struct node{
    	int x,y,w;
    }e[N],b[N];
    int n,m,Q,k,cnt,fa[N],dep[N],mk[N];
    bool ans[N];
    vector<node> q[N];
    vector<int> h[N];
    stack<node> cl;
    int find(int x)
    {return (fa[x]==x)?x:find(fa[x]);}
    void unionn(int x,int y){
    	if(dep[x]>dep[y])swap(x,y);
    	cl.push((node){x,y,dep[y]});
    	fa[x]=y;dep[y]=max(dep[y],dep[x]+1);
    	return;
    }
    bool cmp(node x,node y)
    {return x.w<y.w;}
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++)
    		scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);
    	scanf("%d",&Q);
    	for(int i=1;i<=Q;i++){
    		int k;scanf("%d",&k);
    		for(int j=1,x;j<=k;j++)
    			scanf("%d",&x),b[j]=e[x];
    		sort(b+1,b+1+k,cmp);ans[i]=1;
    		for(int j=1;j<=k;j++){
    			if(b[j].w!=b[j-1].w)
    				++cnt,h[b[j].w].push_back(cnt),mk[cnt]=i;
    			q[cnt].push_back(b[j]);
    		}
    	}
    	sort(e+1,e+1+m,cmp);
    	for(int i=1;i<=n;i++)fa[i]=i;
    	for(int l=1,r=1;l<=m;l=r+1){
    		while(e[r+1].w==e[l].w)r++;
    		int w=e[l].w;
    		while(!cl.empty())cl.pop();
    		for(int j=0;j<h[w].size();j++){
    			int p=h[w][j];
    			for(int i=0;i<q[p].size();i++){
    				int x=q[p][i].x,y=q[p][i].y;
    				x=find(x);y=find(y);
    				if(x==y){ans[mk[p]]=0;break;}
    				unionn(x,y);
    			}
    			while(!cl.empty()){
    				node x=cl.top();cl.pop();
    				dep[x.y]=x.w;fa[x.x]=x.x;
    			}
    		}
    		for(int i=l;i<=r;i++){
    			int x=find(e[i].x),y=find(e[i].y);
    			if(x==y)continue;
    			unionn(x,y);
    		}
    	}
    	for(int i=1;i<=Q;i++)
    		if(ans[i])puts("YES");
    		else puts("NO");
    	return 0;
    }
    
  • 相关阅读:
    LeetCode "Palindrome Partition II"
    LeetCode "Longest Substring Without Repeating Characters"
    LeetCode "Wildcard Matching"
    LeetCode "Best Time to Buy and Sell Stock II"
    LeetCodeEPI "Best Time to Buy and Sell Stock"
    LeetCode "Substring with Concatenation of All Words"
    LeetCode "Word Break II"
    LeetCode "Word Break"
    Some thoughts..
    LeetCode "Longest Valid Parentheses"
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15333696.html
Copyright © 2011-2022 走看看