zoukankan      html  css  js  c++  java
  • [AGC002D] Stamp Rally (并查集+整体二分)

    Description

    给你一个n个点m个条边构成的简单无向连通图,有Q组询问,每次询问从两个点x,y走出两条路径,使这两条路径覆盖z个点,求得一种方案使得路径上经过的变的最大编号最小。

    Input

    第一行两个整数n,m,如题目所述

    接下来m行,每行两个整数x,y描述一条边

    接下来一个整数Q,如题目所述

    接下来Q行,每行三个整数x,y,z,如题目描述

    Output

    Q行,每行一个正整数,如题目描述

    题解:

    先想一想,可以用并查集解决,但 (n^2) 太慢了,于是就想到了整体二分。

    我先是用了一个普通的并查集,结果发现每次都要初始化一遍,T 飞了。

    后来想着可以支持删除,就不能路径压缩了(还是T飞),我了解到了一个黑科技,按秩合并。

    我们合并两棵树的时候,我们把树高小的挂在树高大的下面,这样就能把树高控制在log级别。

    然后我们加边的时候,用栈记录合并的两个节点,分完之后,再从栈中一个个地取出来恢复原样就好了。

    到最后一个点的时候我们再把这条边连上,成功AC。

    对了,我之前加了这个剪枝:

    if(x<y)return;
    

    就是说如果区间里没有数就不往下了,但这会导致有些边没有连,就WA了。

    CODE:

    #include<iostream>
    #include<stack>
    #include<cstdio>
    using namespace std;
    
    int n,m,q,ans[100005];
    int siz[100005],fa[100005];
    struct Edge{
    	int x,y;
    }e[100005];
    struct Question{
    	int x,y,z,id;
    }Q[100005],tmp[100005];
    stack<Edge> s;
    
    int find(int x){
    	if(x==fa[x])return x;
    	return find(fa[x]);
    }
    
    void solve(int l,int r,int x,int y){
    	if(l==r){
    		for(int i=x;i<=y;i++)ans[Q[i].id]=l;
    		int fx=find(e[l].x),fy=find(e[l].y);
    		if(siz[fx]>siz[fy])swap(fx,fy);
    		if(fx!=fy)fa[fx]=fy,siz[fy]+=siz[fx];
    		return;
    	}
    	int mid=l+r>>1;
    	for(int i=l;i<=mid;i++){
    		int fx=find(e[i].x),fy=find(e[i].y);
    		if(siz[fx]>siz[fy])swap(fx,fy);
    		if(fx!=fy){
    			fa[fx]=fy,siz[fy]+=siz[fx];
    			s.push((Edge){fx,fy});
    		}
    	}
    	int tot1=x-1,tot2=0;
    	for(int i=x,size;i<=y;i++){
    		int fx=find(Q[i].x),fy=find(Q[i].y);
    		if(fx==fy)size=siz[fx];
    		else size=siz[fx]+siz[fy];
    		if(size>=Q[i].z)Q[++tot1]=Q[i];
    		else tmp[++tot2]=Q[i];
    	}
    	for(int i=1;i<=tot2;i++)Q[tot1+i]=tmp[i];
    	while(!s.empty()){
    		Edge e=s.top();s.pop();
    		fa[e.x]=e.x,siz[e.y]-=siz[e.x];
    	}
    	solve(l,mid,x,tot1);
    	solve(mid+1,r,tot1+1,y);
    }
    
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++)
    		scanf("%d%d",&e[i].x,&e[i].y);
    	for(int i=1;i<=n;i++)fa[i]=i;
    	for(int i=1;i<=n;i++)siz[i]=1;
    	scanf("%d",&q);
    	for(int i=1;i<=q;i++){
    		scanf("%d%d%d",&Q[i].x,&Q[i].y,&Q[i].z);
    		Q[i].id=i;
    	}
    	solve(1,m,1,q);
    	for(int i=1;i<=q;i++)
    		printf("%d
    ",ans[i]);
    }
    
  • 相关阅读:
    (转)Java 调用 C++ (Java 调用 dll)
    用Gvim建立IDE编程环境 (Windows篇)
    (转)python调取C/C++的dll生成方法
    C/C++协程的实现方式总结
    时钟周期,机器周期,指令周期,总线周期
    (转)MongoDB和Redis区别
    linux cpu占用100%排查
    (转)linux 打开文件数 too many open files 解决方法
    Python下载网页图片
    python抓网页数据【ref:http://www.1point3acres.com/bbs/thread-83337-1-1.html】
  • 原文地址:https://www.cnblogs.com/ezoiLZH/p/9480976.html
Copyright © 2011-2022 走看看