LuoguP4606 [SDOI2018]战略游戏

LuoguP4606 [SDOI2018]战略游戏

题目描述

题目描述
省选临近,放飞自我的小 QQ 无心刷题,于是怂恿小 CC 和他一起颓废,玩起了一款战略游戏。
这款战略游戏的地图由 nn 个城市以及 mm 条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着道路走到任意其他城市。
现在小 CC 已经占领了其中至少两个城市,小 QQ 可以摧毁一个小 CC 没占领的城市,同时摧毁所有连接这个城市的道路。只要在摧毁这个城市之后能够找到某两个小 CC 占领的城市 uu 和 vv,使得从 uu 出发沿着道路无论如何都不能走到 vv,那么小 QQ 就能赢下这一局游戏。
小 QQ 和小 CC 一共进行了 qq 局游戏,每一局游戏会给出小 CC 占领的城市集合 SS,你需要帮小 QQ 数出有多少个城市在他摧毁之后能够让他赢下这一局游戏。

Solution

题意:给定一个 n n n个点 m m m条边的无向图, Q Q Q组询问,每次给出一个点集 { S } \{S\} {S},询问有多少点被删掉之后,点集 { S } \{S\} {S}会存在两点不连通。

建广义圆方树。
建出圆方树之后,让两个点 u , v u,v u,v不连通相当于删掉了除了 u , v u,v u,v以外的某一个 u − > v u->v u>v路径上的圆点。

设点集 { S } = { a 1 , a 2 , . . . , a k } \{S\}=\{a_1,a_2,...,a_k\} {S}={a1,a2,...,ak}
点集中的点按 d f s dfs dfs序排序,
令相邻点对(包括 ( a 1 , a k ) (a_1,a_k) (a1,ak))之间路径的圆点数量和为 a n s ans ans
总的答案即为 a n s / 2 − k + [ L C A ( a 1 , a k ) 为 圆 点 ] ans/2-k+[LCA(a_1,a_k)为圆点] ans/2k+[LCA(a1,ak)]

时间复杂度 O ( n lg ⁡ n ) O(n\lg n) O(nlgn)
这题也可以用虚树做,其实和上述方法差不多,只是实现方式不同。

#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>

#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se second

using namespace std;

template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }

typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;

const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=1e9+7;
const int MAXN=200005;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{
	int f=1,x=0; char c=getchar();
	while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
	while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
	return x*f;
}
vector<int> e[MAXN],E[MAXN];
int n,m,dep[MAXN],Log[MAXN],a[MAXN];
int dfn[MAXN],low[MAXN],stk[MAXN],fa[MAXN][20],dist[MAXN],bnum=0,top=0,DFN=0;
int compare(int x,int y) { return dfn[x]<dfn[y]; }
void add_edge(int u,int v){ /*cout<<"Edge:"<<u<<" "<<v<<endl;*/ E[u].PB(v),E[v].PB(u); }
void tarjan(int x,int father)
{
	dfn[x]=low[x]=++DFN;
	stk[++top]=x;
	for (auto v:e[x])
	{
		if (v==father) continue;
		if (dfn[v]) { low[x]=min(low[x],dfn[v]); continue; }
		tarjan(v,x);
		low[x]=min(low[x],low[v]);
		if (low[v]>=dfn[x])
		{
			int y;
			bnum++;
			add_edge(n+bnum,x);
			while (y=stk[top--])
			{
				add_edge(n+bnum,y);
				if (v==y) break;
			}
		}
	}
}
void dfs(int x,int father)
{
//	cout<<x<<" "<<father<<endl;
	dfn[x]=++DFN;
	dep[x]=dep[father]+1;
	fa[x][0]=father;
	for (int i=1;i<=Log[dep[x]];i++) fa[x][i]=fa[fa[x][i-1]][i-1];
	for (auto v:E[x])
		if (v!=father) dist[v]=dist[x]+(v<=n),dfs(v,x);
}
int get_LCA(int x,int y)
{
	if (dep[x]<dep[y]) swap(x,y);
	for (int i=Log[dep[x]];i>=0;i--)
		if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
	if (x==y) return x;
	for (int i=Log[dep[x]];i>=0;i--)
		if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
int getans(int x,int y)
{
	int lca=get_LCA(x,y);
//	cout<<x<<":"<<dist[x]<<" "<<y<<":"<<dist[y]<<" "<<lca<<":"<<dist[lca]<<endl;
//	cout<<dep[x]<<" "<<dep[y]<<" "<<dep[lca]<<endl; 
	return dist[x]+dist[y]-dist[lca]*2;
}
void Init()
{
	bnum=0,top=0,DFN=0;
	memset(dist,0,sizeof dist);
	memset(dfn,0,sizeof dfn);
	memset(fa,0,sizeof fa);
	memset(low,0,sizeof low);
	for (int i=1;i<=n*2;i++) e[i].clear(),E[i].clear();
}
int main()
{
//	freopen("c.in","r",stdin); 
	n=read(),m=read();
	int q=read();
	Init();
	for (int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		e[u].PB(v);
		e[v].PB(u);
	}
	tarjan(1,0);
	dep[0]=-1,Log[1]=0,dist[1]=0;
	for (int i=2;i<=n*2;i++) Log[i]=Log[i>>1]+1; 
	DFN=0;
	dfs(1,0);
	while (q--)
	{
		int k=2;
		a[1]=read(),a[2]=read();
		sort(a+1,a+k+1,compare);
		int ans=getans(a[1],a[k]);
		for (int i=1;i<k;i++) ans+=getans(a[i],a[i+1]);
//		cout<<"Ans:"<<ans<<endl;
		printf("%d\n",ans/2-k+(get_LCA(a[1],a[k])<=n));
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值