题解 [UOJ #461] 新年的Dog划分
艹感觉自己在划大水
所以这题是水过去的
我把题读成了什么呢?每次选若干个点对,将这些点对及与之相关的边全部删除
于是自闭一上午
等通过题解意识到自己题读错了已经没意愿再好好想题了
那么暴力可以枚举左部点集,仅保留两点集之间的边看是否连通
因为保证是二分图了嘛,可以反证这样合法(水博
然后一个好一点的暴力是不断删边
不影响连通性就删,影响就留着,这样可以找到一棵生成树
那么对生成树黑白染色即可
check 合法就是 ban 掉左右部点之间除生成树上的边,看生成树上是否有环(能不能再从树上 ban 条边)
然后删边可以二分下一个树边在哪,但是 \(\log n^2\approx2\log n\) 过不去
于是对每个点做这个事情,就只需要在 \(n\) 条边中二分了
然后你发现这样二分每个点的最后一次二分可能不对应一条树边,所以又带了一个 2 的常数
我的解决方法是每次二分之前 check 一下剩下的边都删掉是否合法
这样复杂度应该是 \(O(n\log n+2n+n)\),\(2n\) 是上面这个 check,最后那个 \(n\) 是回代找环
貌似正解是 BFS 处理什么的(咕
点击查看代码
#include <bits/stdc++.h>
#include "graph.h"
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define pb push_back
int n;
bool vis[N], col[N];
vector<int> to[N], ans;
map<pair<int, int>, bool> intr;
vector<pair<int, int>> base, tree;
bool query(std::vector<std::pair<int, int>> banned_edges);
void dfs(int u, int c) {
// cout<<"dfs: "<<u<<endl;
vis[u]=1; col[u]=c;
vector<pair<int, int>> sta, t;
for (int i=0; i<n; ++i) if (!vis[i]) sta.pb({u, i});
if (!sta.size()) return ;
int lst=0;
while (lst<=sta.size()-1) {
int l=lst, r=sta.size()-1, mid;
t=base;
for (int j=lst; j<sta.size(); ++j) t.pb(sta[j]);
if (query(t)) break;
while (l<=r) {
mid=(l+r)>>1; t=base;
for (int j=lst; j<=mid; ++j) t.pb(sta[j]);
if (query(t)) l=mid+1;
else r=mid-1;
}
if (l>=sta.size()) break;
tree.pb(sta[l]), to[u].pb(sta[l].sec);
for (int i=lst; i<l; ++i) base.pb(sta[i]);
lst=l+1;
}
for (auto it:to[u]) if (!vis[it]) dfs(it, col[u]^1);
}
vector<int> check_bipartite(int vsize) {
n=vsize;
dfs(0, 0);
assert(tree.size()==n-1);
// cout<<"tree: "; for (auto it:tree) cout<<"("<<it.fir<<','<<it.sec<<") "; cout<<endl;
for (auto it:tree) intr[{min(it.fir, it.sec), max(it.fir, it.sec)}]=1;
vector<pair<int, int>> tem, t;
for (int i=0; i<n; ++i)
for (int j=i+1; j<n; ++j) if (col[i]!=col[j])
if (intr.find({i, j})==intr.end())
tem.pb({i, j});
for (auto it:tree) {
t=tem; t.pb(it);
if (query(t)) return vector<int>();
}
vector<int> ans;
for (int i=0; i<n; ++i) if (col[i]) ans.pb(i);
return ans;
}