zoukankan      html  css  js  c++  java
  • 【洛谷P4606】战略游戏

    题目

    题目链接:https://www.luogu.com.cn/problem/P4606
    省选临近,放飞自我的小 (Q) 无心刷题,于是怂恿小 (C) 和他一起颓废,玩起了一款战略游戏。
    这款战略游戏的地图由 (n) 个城市以及 (m) 条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着道路走到任意其他城市。
    现在小 (C) 已经占领了其中至少两个城市,小 (Q) 可以摧毁一个小 (C) 没占领的城市,同时摧毁所有连接这个城市的道路。只要在摧毁这个城市之后能够找到某两个小 (C) 占领的城市 (u)(v),使得从 (u) 出发沿着道路无论如何都不能走到 (v),那么小 (Q) 就能赢下这一局游戏。
    (Q) 和小 (C) 一共进行了 (q) 局游戏,每一局游戏会给出小 (C) 占领的城市集合 (S),你需要帮小 (Q) 数出有多少个城市在他摧毁之后能够让他赢下这一局游戏。
    (1 ≤ T ≤ 10)(2 ≤ n ≤ 10^5)(n-1≤m≤2×10^5)(1 ≤ q ≤ 10^5),对于每组测试数据,有 (∑|S| ≤ 2 × 10^5)

    思路

    等价于询问任意选择两个集合内的点,其路径上必经点的并集大小。
    Tarjan 把点双缩一下,建出圆方树,然后每一次询问将点按照圆方树上点的 dfn 排序。
    枚举 dfn 序相邻两个点,他们之间的贡献为路径上圆点的数量。所以我们把圆点的权值设为 (1),方点为 (0),然后求出 LCA 就可以知道路径上权值之和了。注意需要减去 (u,v) 两个点的贡献 (2)
    但是这样计算容易算重且不好容斥,所以我们把每一个点的权值放到他和他父节点之间的道路上,这样每一条路会被计算两次。最终答案除以 (2) 即可。但是我们这样会漏掉 dfn 序最小和最大的点 LCA 的贡献。所以再加回来即可。
    时间复杂度 (O(sum nlog n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=400010,LG=20;
    int Q1,Q,n,m,t,ans,a[N];
    
    struct edge
    {
    	int next,to;
    };
    
    struct Tree
    {
    	int tot,head[N],dfn[N],cnt[N],dep[N],f[N][LG+1];
    	edge e[N];
    	
    	void add(int from,int to)
    	{
    		e[++tot]=(edge){head[from],to};
    		head[from]=tot;
    	}
    	
    	void dfs(int x,int fa)
    	{
    		dfn[x]=++tot; dep[x]=dep[fa]+1;
    		cnt[x]=cnt[fa]+(x<=n);
    		f[x][0]=fa;
    		for (int i=1;i<=LG;i++)
    			f[x][i]=f[f[x][i-1]][i-1];
    		for (int i=head[x];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			if (v!=fa) dfs(v,x);
    		}
    	}
    	
    	int lca(int x,int y)
    	{
    		if (dep[x]<dep[y]) swap(x,y);
    		for (int i=LG;i>=0;i--)
    			if (dep[f[x][i]]>=dep[y]) x=f[x][i];
    		if (x==y) return x;
    		for (int i=LG;i>=0;i--)
    			if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    		return f[x][0];
    	}
    }T;
    
    bool cmp(int x,int y)
    {
    	return T.dfn[x]<T.dfn[y];
    }
    
    struct Graph
    {
    	int tot,cnt,head[N],dfn[N],low[N];
    	edge e[N];
    	stack<int> st;
    	
    	void add(int from,int to)
    	{
    		e[++tot]=(edge){head[from],to};
    		head[from]=tot;
    	}
    	
    	void tarjan(int x)
    	{
    		dfn[x]=low[x]=++tot;
    		st.push(x);
    		for (int i=head[x];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			if (!dfn[v])
    			{
    				tarjan(v);
    				low[x]=min(low[x],low[v]);
    				if (low[v]>=dfn[x])
    				{
    					int y; cnt++;
    					do {
    						y=st.top(); st.pop();
    						T.add(cnt,y); T.add(y,cnt);
    					}while (y!=v);
    					T.add(x,cnt); T.add(cnt,x);
    				}
    			}
    			else low[x]=min(low[x],dfn[v]);
    		}
    	}
    }G;
    
    void prework()
    {
    	while (G.st.size()) G.st.pop();
    	memset(G.head,-1,sizeof(G.head));
    	memset(G.dfn,0,sizeof(G.dfn));
    	memset(T.head,-1,sizeof(T.head));
    	G.tot=T.tot=0;
    }
    
    int main()
    {
    	scanf("%d",&Q1);
    	while (Q1--)
    	{
    		prework();
    		scanf("%d%d",&n,&m);
    		for (int i=1,x,y;i<=m;i++)
    		{
    			scanf("%d%d",&x,&y);
    			G.add(x,y); G.add(y,x);
    		}
    		G.tot=0; G.cnt=n;
    		G.tarjan(1);
    		T.tot=0;
    		T.dfs(1,0);
    		scanf("%d",&Q);
    		while (Q--)
    		{
    			scanf("%d",&t);
    			for (int i=1;i<=t;i++)
    				scanf("%d",&a[i]);
    			sort(a+1,a+1+t,cmp);
    			a[++t]=a[1]; ans=0;
    			for (int i=2;i<=t;i++)
    			{
    				int p=T.lca(a[i],a[i-1]);
    				ans+=T.cnt[a[i]]+T.cnt[a[i-1]]-2*T.cnt[p]-2;
    			}
    			printf("%d
    ",ans/2+(T.lca(a[1],a[t-1])<=n));
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    curl.class.php方便使用
    php发送邮件
    php部分被弃用方法的代替方法
    正则表达式匹配
    php xml常用函数的集合及四种方法
    PHP5中使用PDO连接数据库的方法
    http协议的状态码
    MySQL数据库中的Date,DateTime和TimeStamp类型
    php中json_decode()和json_encode()的使用方法
    UIIabel自适配高度
  • 原文地址:https://www.cnblogs.com/stoorz/p/14278985.html
Copyright © 2011-2022 走看看