[BZOJ 2438] 中山市选2011 杀人游戏 · Tarjan

一开始想到用并查集直接求联通,但是这样貌似并不可行。。。

每调查一个人,都会增加1/n的被杀概率,所以要尽量少调查人。

如果我们调查了一个人,那么和他认识的人我们全都能知道,以此类推,向下的人都可以知道,所以我们只要求入度为0的点的个数,这些人我们只能通过直接询问他才能知道是不是杀手,另外的人都可以通过这些人知道。

对于一个环里的人,我们只要知道一个点就能摸清所有点,所以先Tarjan缩环。

另外这题还有一个特别精巧的地方,这n个点构成了scc个分量,有一个分量只包含一个点,如果这个分量没有出边,或者他连向的点都可以通过其他分量到达,那么我们只要查其他scc-1个点就可以摸清n-1个点,最后一个点自然也清楚了,这时候要不算这个点的询问次数。(只有可能存在一个点,因为知道n-2个点并不能摸清剩下2个点)

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <iostream>
using namespace std;

const int N=300005;
struct arr{
	int node,nxt;
}e1[N],e2[N];
int head1[N],head2[N],tot1,tot2;
int n,m,sumin[N],sumout[N],ans,num[N];

void add1(int x,int y){
	e1[++tot1].node=y;
	e1[tot1].nxt=head1[x];
	head1[x]=tot1;
}
void add2(int x,int y){
	e2[++tot2].node=y;
	e2[tot2].nxt=head2[x];
	head2[x]=tot2;
	sumin[y]++;sumout[x]++;
}

int dfn[N],low[N],tim,stack[N],top,scc,belong[N];
bool check[N];

void tarjan(int t){
	dfn[t]=low[t]=++tim;
	check[t]=1; stack[++top]=t;
	int u,v;
	for (int i=head1[t];i;i=e1[i].nxt){
		int x=e1[i].node;
		if (!dfn[x]) {
			tarjan(x);
			low[t]=min(low[t],low[x]);
		}
		else 
			if (check[x]) low[t]=min(low[t],dfn[x]);
	}
	if (dfn[t]==low[t]){
		int now=0;scc++;
		while (now!=t){
			now=stack[top--];
			belong[now]=scc;
			check[now]=0;
			num[scc]++;
		}
	}
}

void rebuild(){
	bool vis[N];
	memset(vis,0,sizeof vis);	//防止加重边 
	for (int i=1;i<=n;i++){
		for (int j=head1[i];j;j=e1[j].nxt){
			int u=belong[i],v=belong[e1[j].node];
			if (u!=v && !vis[v]) {vis[v]=1;add2(u,v);}
		}
		for (int j=head1[i];j;j=e1[j].nxt){
			int u=belong[i],v=belong[e1[j].node];
			if (u!=v) vis[v]=0;
		}
	}
}

bool judge(int i){
	if (sumin[i] || num[i]!=1) return 0;	//入度不为0或者联通块中不止一个点则跳出
	for (int j=head2[i];j;j=e2[j].nxt)
		if (sumin[e2[j].node]==1) return 0;
	return 1;
}

int main(){
	cin>>n>>m;
	for (int i=1;i<=m;i++) {
		int x,y;cin>>x>>y;
		add1(x,y);
	}
	for (int i=1;i<=n;i++)
		if (!dfn[i]) tarjan(i);
	rebuild();
	for (int i=1;i<=scc;i++)
		if (!sumin[i]) ans++;
	for (int i=1;i<=scc;i++)
		if (judge(i)) {
			ans--;break;
		}
	printf("%.6lf\n",(n-ans)*1.0/n);
	return 0;
}


引用:中山市是一个不设区的地级市,它包含了6个街道和18个镇。其中,有石岐街道、东区街道、西区街道、南区街道、五桂山街道、火炬开发区街道等。 引用:根据描述,这个游戏是一个给定了起始位置和目标位置的移动游戏。在一个n * m的棋盘上,棋盘上有两种不同的格子,分别用#和@表示。小明每次可以向上、下、左、右四个方向移动一格,如果移动到相同类型的格子上,费用为0,否则费用为1。问题要求计算从起始位置移动到目标位置所需的最小花费。输入包含多组数据,每组数据的格式为:第一行是两个整数n和m,表示棋盘的行数和列数;接下来的n行每行包含m个格子;最后一行是四个整数x1、y1、x2、y2,表示起始位置和目标位置的坐标。当输入的n和m都为0时,表示输入结束。输出每组数据的最小花费,每组数据独占一行。 引用:根据样例输入和输出,可以看出其中一个示例的输入是2行2列的棋盘,棋盘上有两种格子@@和@#;起始位置是(0, 1),目标位置是(1, 0)。根据游戏规则,需要计算从起始位置到目标位置的最小花费。输出为2。 根据以上信息,中山市游戏之间没有直接关联。请问你是想了解中山市举情况吗?如果是的话,请提供更多的相关信息。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [不设区的地级市之中山市geoJSon可直接使用](https://download.csdn.net/download/weixin_36323996/12833075)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【中山市2009】【BZOJ2464】小明的游戏](https://blog.csdn.net/CreationAugust/article/details/48679593)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值