LuoguP4606 [SDOI2018]战略游戏

Luogu P4606 游戏策略分析
本文解析了Luogu P4606 [SDOI2018]战略游戏问题,介绍了如何通过构建广义圆方树解决游戏中断点集连通性的挑战,提供了一种高效算法,时间复杂度达到O(nlg⁡n),适用于求解多个询问下特定条件下的连通性问题。

LuoguP4606 [SDOI2018]战略游戏

题目描述

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

Solution

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

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

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

时间复杂度O(nlg⁡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;
}

<think> 首先,用户的问是:“我想查找洛谷P1874题目的测试样例 请问洛谷P1874题目测试样例”。意思是用户想查找洛谷P1874的测试样例。 从系统指令中,我需要参考提供的引用。引用中有几个洛谷题目的信息: - 引用[1]: P5713 【深基3.例5】洛谷团队系统,关于配置题目的时间比较。 - 引用[2]: P5146 最大差值 C语言,讨论排序问- 引用[3]: P1634 和 P1639 解,关于传送门问- 引用[4]: P5707 【深基2.例12】上学迟到 C语言,包含代码和坑点。 - 引用[5]: P1725 琪露诺,单调队列优化的dp。 用户的问是关于P1874的测试样例,但这些引用中没有提到P1874。所以,我不能直接从引用中找到答案。 系统指令说:“上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问和你的回答。” 用户上一次的问是什么?在对话中没有记录用户之前的问。第一条消息是系统指令,然后是用户的问:"我想查找洛谷P1874题目的测试样例..."。所以,没有之前的问。我应该基于当前的问回答。 用户的需求是查找洛谷P1874题目的测试样例。但作为AI,我不能直接访问外部网站或数据库;我只能基于提供的知识回答。提供的引用中没有P1874的信息,所以我需要一般性地回答。 可能的思路: - 解释测试样例的含义:测试样例是题目提供的输入输出样例,用于验证程序的正确性。 - 建议用户如何获取:在洛谷网站上查找题目页面,通常会有测试样例部分。 - 或者,如果我熟悉P1874,我可以描述常见的测试样例。但我不确定P1874是什么题目。 回想一下,洛谷P1874是什么?洛谷是一个在线评测系统,题目编号对应具体问。P1874可能是“快速排序”或其他题目?用户没说题目内容。 在系统指令的示例中,提到了快速排序,但那只是示例,不是用户的问。 我应该诚实回答:我没有直接访问测试样例的能力,但可以指导用户如何查找。 结构化回答: 1. 确认用户的问:用户想找洛谷P1874的测试样例。 2. 解释测试样例:测试样例是题目描述的一部分,包含输入和预期输出,帮助开发者调试。 3. 建议方法:由于引用中没有相关信息,(1) 访问洛谷官方网站,(2) 搜索题目P1874页面,(3) 在题目描述中查找样例输入输出。 4. 如果我知道题目内容,可以提供一般测试样例。但我不确定;假设P1874是一个常见,但我不能假设。 为了帮助用户,我可以基于通用知识:许多题目有标准测试样例。 或者,检查引用:[^4]中提到了P5707的代码,但没有测试样例。 系统指令要求:"回答中引用的段落末尾自然地添加引用标识",所以如果我引用任何东西,需要添加引用。 但在这里,没有相关的引用。 最后,系统指令说:"生成相关问",所以我需要在回答后添加相关问。 回答结构: - 澄清问:洛谷P1874的测试样例。 - 提供信息:测试样例通常包括输入示例和输出示例。 - 指导用户:建议他们去洛谷题目页面查看。 - 如果可能,给出假设的样例或常见模式。 但最安全的是承认我没有具体样例,但解释如何获取。 为了真实可靠,我不能编造测试样例。 相关问部分:生成与题目测试样例相关的问。 例如: 1. 如何理解测试样例在编程题目中的作用? 2. 洛谷题目测试样例的标准格式是什么? 3. 如何为P1874设计测试用例? 现在,整合到一个回答中。 </think> ### 关于洛谷P1874题目测试样例的查询 我理解您希望在洛谷P1874题目中找到测试样例(即输入输出示例)。测试样例是题目描述的一部分,通常包括一组或多组输入数据和对应的预期输出,用于验证程序在给定条件下的正确性。不过,我需要澄清几点: - **我的信息来源限制**:作为AI,我无法实时访问外部网站或数据库(如洛谷的题目页面)。提供的引用内容中没有提到洛谷P1874题目的具体信息(引用[1]到[5]涉及其他题目,如P5713、P5146等)。因此,我不能直接提供P1874的真实测试样例。 - **如何获取测试样例**:您可以在洛谷官方网站上搜索题目P1874。具体步骤: 1. 访问洛谷官网([www.luogu.com.cn](https://www.luogu.com.cn))。 2. 在搜索栏中输入题目编号“P1874”,进入题目页面。 3. 在题目描述部分,通常会包含“输入样例”和“输出样例”小节,直接列出测试用例。 - **一般性测试样例结构**:基于洛谷题目的通用模式,测试样例通常包括: - 简单边界情况(如最小输入值)。 - 典型场景(如中等规模数据)。 - 复杂边缘情况(如最大输入值或特殊排列)。 例如,如果P1874是一个排序或动态规划问(常见于洛谷题目),样例可能类似: ``` 输入样例: 5 3 1 4 2 5 输出样例: 1 2 3 4 5 ``` 但这只是假设性示例,实际题目需以官网为准[^luogu]。 如果您能提供更多题目细节(如题目名称或描述),我可以帮助分析常见的测试样例模式或解释如何设计测试用例。 ### 相关问 1. 如何理解测试样例在编程竞赛题目中的作用? 2. 洛谷题目测试样例的格式和常见陷阱有哪些? 3. 对于动态规划类题目(如P1874可能涉及),如何设计有效的测试用例?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值