CF553D Nudist Beach
题目地址
Solution
一道比较简单的二分
先分析一下题目性质:任意城市被敌军占领,其周围城市的安全度都会降低\(\implies\)
我们在选取城市时,应遵循这样一个原则:所有能够被己方控制的城市都应当让己方控制。
这里的“能够”当然只在二分答案的限制下才有意义。
那么,解决方法就很简单了。
利用队列,先将必须被敌军占领(有堡垒)的城市入队,将队列中的城市一个个拿出来更新其周围城市的安全度,如果更新后安全度小于\(mid\)就将这个城市入队。
如果最后还有城市未被敌军占领,\(l = mid\)否则,\(r = mid\),最终\(l\)为最大安全度。
另外,这道题对精度的要求比较高,我设置的精度是\(10^{-9}\)。\(10^{-8}\)以上大概就过不了了。
Code
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 10;
const double eps = 1e-9;
int n,m,k,siz[MAXN];
int num[MAXN],d[MAXN];//d:某城市周围被敌军占领的城市数量。
bool vis[MAXN];
vector<int> e[MAXN];
queue<int> q;
bool judge(double mid) {
memset(vis,false,sizeof(vis));
memset(d,false,sizeof(d));
for (int i = 1; i <= k; i++) {
q.push(num[i]);vis[num[i]] = true;
for (int j = 0; j < siz[num[i]]; j++) {
d[e[num[i]][j]]++;
}
}
while (!q.empty()) {
int u = q.front();q.pop();
int len = e[u].size();
for (int i = 0; i < len; i++) {
int v = e[u][i];
if (vis[v]) continue;
double nu = ((siz[v] - d[v]) * 1.0 / siz[v]);
if (nu < mid) {
vis[v] = true;
for (int j = 0; j < siz[v]; j++) {
d[e[v][j]]++;
}
q.push(v);
}
}
}
for (int i = 1; i <= n; i++) if (!vis[i]) return true;
return false;
}
int main() {
scanf("%d%d%d",&n,&m,&k);
for (int i = 1; i <= k; i++) {
scanf("%d",&num[i]);
}
for (int i = 1; i <= m; i++) {
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
for (int i = 1; i <= n; i++) siz[i] = e[i].size();
double l = 0,r = 1.0;
while (r - l > eps) {
double mid = (l + r) / 2;
if (judge(mid)) l = mid;
else r = mid;
}
memset(vis,false,sizeof(vis));
judge(l);
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
cnt++;
}
}
cout << cnt << endl;
for (int i = 1; i <= n; i++) {
if (!vis[i]) printf("%d ",i);
}
return 0;
}