zoukankan      html  css  js  c++  java
  • P4606 [SDOI2018]战略游戏 圆方树+虚树

    题意:

    戳这里

    分析:

    • 前置芝士:圆方树,虚树

    题意让我们求出,哪些点删掉后会使得点集里某两个点不再联通

    暴力 (O(qn^2)) , 就是每次询问直接枚举点, (O(n)) check 一下是否合法

    正解:

    对于这种图上割点 的问题,我们可以建出圆方树转化在树上去做,我们建出圆方树,利用圆方树的性质:原图上任意两点的割点一定是他们在圆方树上路径上的原点,所以我们只要求出在点集中某两个点的路径上的原点的个数,对于这种给定点集总和的题,我们按照常见思路建出虚树,直接求一下虚树的大小

    tip: 对于求虚树大小的这种操作,其实都不需要建出虚树,加边的时候直接统计新加进来多少个节点

    代码:

    #include<bits/stdc++.h>
    #define pii pair<int,int>
    #define mk(x,y) make_pair(x,y)
    #define lc rt<<1
    #define rc rt<<1|1
    #define pb push_back
    #define fir first
    #define sec second
    #define inl inline
    #define reg register
    
    using namespace std;
    
    namespace zzc
    {
    	inline int read()
    	{
    		int x=0,f=1;char ch=getchar();
    		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    		return x*f;
    	}
    	
    	const int maxn = 4e5+5;
    	const int maxm = 8e5+5;
    	int t,n,m,qt,top,col,ans,idx;
    	int dfn[maxn],st[maxn],low[maxn],dep[maxn],fa[maxn][20],tmp[maxn];
    
    	struct graph
    	{
    		int cnt;
    		int head[maxn];
    		struct edge
    		{
    			int to,nxt;
    		}e[maxm];
    		void clear()
    		{
    			cnt=0;
    			memset(head,0,sizeof(head));
    		}
    		void add(int u,int v)
    		{
    			e[++cnt].to=v;
    			e[cnt].nxt=head[u];
    			head[u]=cnt;
    		}
    		void add_edge(int u,int v)
    		{
    			add(u,v);add(v,u);
    		}
    	}t1,t2;
    
    	void tarjan(int u,int ff)
    	{
    		dfn[u]=low[u]=++idx;
    		st[++top]=u;
    		for(int i=t1.head[u];i;i=t1.e[i].nxt)
    		{
    			int v=t1.e[i].to;
    			if(v==ff) continue;
    			if(!dfn[v])
    			{
    				tarjan(v,u);
    				low[u]=min(low[u],low[v]);
    				if(low[v]>=dfn[u])
    				{
    					t2.add_edge(++col,u);
    					for(;st[top+1]!=v;top--) t2.add_edge(col,st[top]);	
    				}
    			}
    			else low[u]=min(low[u],dfn[v]);
    		}
    	}
    	
    	void dfs(int u,int ff)
    	{
    		dfn[u]=++idx;fa[u][0]=ff;dep[u]=dep[ff]+1;
    		for(int i=t2.head[u];i;i=t2.e[i].nxt)
    		{
    			int v=t2.e[i].to;
    			if(v==ff) continue;
    			dfs(v,u);
    		}
    	}
    	
    	int lca(int x,int y)
    	{
    		if(dep[x]<dep[y]) swap(x,y);
    		for(int i=19;i>=0;i--) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    		if(x==y) return x;
    		for(int i=19;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    		return fa[x][0];
    	}
    	
    	bool cmp(int x,int y)
    	{
    		return dfn[x]<dfn[y];
    	}
    	
    	void work()
    	{
    		int a,b;
    		t=read();
    		while(t--)
    		{
    			t1.clear();t2.clear();for(int i=1;i<=col;i++) dfn[i]=0;
    			n=read();m=read();col=n;
    			for(int i=1;i<=m;i++)
    			{
    				a=read();b=read();
    				t1.add_edge(a,b);
    			}
    			top=0;idx=0;tarjan(1,0);
    			idx=0;dfs(1,0);
    			for(int j=1;j<=19;j++) for(int i=1;i<=col;i++) fa[i][j]=fa[fa[i][j-1]][j-1];
    			qt=read();
    			while(qt--)
    			{
    				a=read();ans=0;top=0;
    				for(int i=1;i<=a;i++) tmp[i]=read();
    				sort(tmp+1,tmp+a+1,cmp);
    				for(int i=1;i<=a;i++)
    				{
    					if(!top) st[++top]=tmp[i];
    					else
    					{
    						int x=lca(st[top],tmp[i]);
    						ans+=((dep[st[top]]+1)>>1)-((dep[x]+1)>>1);
    						while(top&&dep[st[top]]>=dep[x]) top--;
    						st[++top]=x;st[++top]=tmp[i];
    					}
    				}
    				ans+=((dep[st[top]]+1)>>1)-(dep[st[1]]>>1)-a;
    				printf("%d
    ",ans);
    			}
    		}
    	
    	}
    
    }
    
    int main()
    {
    //	freopen("01.in","r",stdin);
    //	freopen("ans.out","w",stdout);
    	zzc::work();
    	return 0;
    }
    
    
  • 相关阅读:
    Nginx的configure各项中文说明
    nginx+keepalived高可用及双主模式
    nginx实现请求的负载均衡 + keepalived实现nginx的高可用
    Keepalived详细介绍简介
    keepalived vip漂移基本原理及选举算法
    keepalived脑裂问题查找
    计算机网络的三种通讯模式(单播,广播,组播)
    二层交换机、三层交换机、路由器
    Linux /var/log下的各种日志文件详解
    报文、帧、数据包等的区别
  • 原文地址:https://www.cnblogs.com/youth518/p/14272897.html
Copyright © 2011-2022 走看看