【UOJ#26】Game
题目
题目链接:https://uoj.ac/problem/26
佳是一个喜欢做游戏的小男生。当有人问问题时,他更喜欢通过玩游戏的方式作答,而不是直接回答。健佳碰到了他的朋友梅玉,跟她讲了台湾的航空网。在台湾有 \(n\) 个城市(编号为 \(0,\cdots,n−1\)),其中有些城市之间有航线。每个航线连接两个城市,并且是双向的。
梅玉问健佳,是否任意两个城市之间都可以坐飞机互达(直接或间接),健佳不想直接回答,而是要通过做游戏的方式来告诉她。梅玉可以问"城市 \(u\) 和 \(v\) 之间有直接航线吗?",健佳会立刻直接回答该问题。梅玉会询问每对城市恰好一次,因此总计会有 \(r = \frac{n (n−1)}{2}\) 个问题。如果由前 \(i\)(\(i<r\))个问题的答案可以推断出整个航空网是否连通,也就是说,是否任意一对城市之间都可以坐飞机互达(直接或间接),梅玉就获胜。否则意味着她需要知道全部 \(r\) 个回答,此时健佳获胜。
为了让游戏更好玩,他们俩同意,健佳可以不要管台湾的真实航空网,而是可以随着游戏的进展而编造航空网,也就是根据梅玉此前的提问来决定此后如何作答。你的任务是,通过决定健佳如何回答,来帮助他赢得游戏。
请写出一个可以帮助健佳获胜的程序。注意,无论是梅玉还是健佳,都不知道对方的策略。梅玉可以以任意的顺序来询问城市对,而健佳必须在不清楚后面提问的前提下立刻给出回答。你需要实现下面的两个函数:
initialize(n)—— 我们会先调用你的initialize函数。参数 \(n\) 是城市数目。hasEdge(u, v)—— 接着我们会调用hasEdge函数 \(r = n(n - 1)/2\) 次。这些调用代表了梅玉的提问,顺序与她提问的次序相同。你必须回答在城市 \(v\) 和 \(u\) 之间是否有直接航线。具体而言,返回值 \(1\) 表示有,\(0\) 表示没有。
\(n\leq 1500\)。
思路
早上在洛谷讨论区看到这道题,说是 UOJ 有交互,就跑到 UOJ 上做了。
考虑最后一次询问,在这次询问之前,要求无法得知整张图是否连通。那么最后一次询问肯定是询问两个连通块之间的边。
为了让这两个连通块之间之前一直不连通,之前询问的所有连接这两个连通块的边都需要回答 \(0\)。
但是因为是交互的形式,我们并不清楚最后是哪两个连通块。所以需要找到一种无论最后是哪两个连通块都能满足条件的方式:对于一条边 \((u,v)\),我们连接它,当且仅当这条边是这两个连通块之间的最后一条边。
用并查集维护连通块,每次合并的时候更新一下这个连通块与其他连通块之间的边数即可。
时间复杂度 \(O(n^2)\)。
后面看了一下洛谷题解发现,其实只需要把每一条边的方向定为从大到小,每次询问一条边 \((u,v)\),设 \(u>v\),那么只需要判断一下 \(u\) 之前是不是已经询问过 \(u-1\) 次就行了。这样并查集都不用写。
代码
#include "game.h"
#include <bits/stdc++.h>
using namespace std;
const int N=1510;
int m,father[N],siz[N],cnt[N][N];
void initialize(int n)
{
m=n;
for (int i=1;i<=n;i++)
father[i]=i,siz[i]=1;
}
int find(int x)
{
return x==father[x]?x:father[x]=find(father[x]);
}
void merge(int x,int y)
{
for (int i=1;i<=m;i++)
cnt[x][i]+=cnt[y][i],cnt[i][x]+=cnt[i][y];
siz[x]+=siz[y]; father[y]=x;
}
int hasEdge(int u,int v)
{
u=find(u+1); v=find(v+1);
cnt[u][v]++; cnt[v][u]++;
if (cnt[u][v]==siz[u]*siz[v])
return merge(u,v),1;
return 0;
}

浙公网安备 33010602011771号