2024/6/28UPD CSDN又擅自给我改VIP文章:(
分析
由于要求的是连通块数量最小,那么我们首先可以想到生成树,如果将生成树染成一个颜色,那么这个颜色的连通块就只有一个了,那就做完了。
为什么染成生成树一定最优呢?
我们令红边的连通块数记为 c 1 c_1 c1,记蓝边连通块数记为 c 2 c_2 c2。
首先,我们观察到一点,假设红边组成的子图中存在一个环,我们将环上的其中一条边染成蓝色,
c
1
c_1
c1 不会发生变化,而
c
2
c_2
c2 最多会减少
1
1
1,因此最终答案中一定不能存在任何一个环。
而如果不存在这样的一个环,则如果将一条红边染成蓝色, c 1 c_1 c1 最多增加 1 1 1, c 2 c_2 c2 最少减少 1 1 1。
因此我们让任何一个颜色组成一棵生成树一定是不亏的。
我们假定组成生成树的是红边。由于树的性质,我们知道他一共包含了 n − 1 n - 1 n−1 条边,而题目保证 m ⩽ n + 2 m \leqslant n + 2 m⩽n+2,这就说明了蓝边最多只有 3 3 3 条边,也就是蓝边最多只可能形成一个环 ( 因为要形成两个环至少需要 5 5 5 条边 )。因此,我们就只需要考虑怎么把这个环断开就行了 ( 至于为什么,看上面 )。
实际上这个问题非常简单,因为我们的红色边已经构成了一棵生成树了,因此我们只需要找到这换上三个点当中最深的那个,将他原本连接的任意一条蓝色边染成红色,再将他与他原本生成树上的父亲的红色边染成蓝色即可。这样做既可以保证红色边仍然形成一棵生成树,也可以把环断开,具体方法如图。
至于为什么随便断环会错我也不知道,希望有大佬能证出来Qwq。
那么这样就很简单了,我们只需要 DFS 求出生成树,再断环即可。
注意
千万别用memset!!!
千万别用memset!!!
千万别用memset!!!
代码
小心哦~
代码里是有坑的哦~
放心~
不是算法上的坑~
//省略快读和头文件
int n, m;
struct Edge {
int hd[MAXN];
int nxt[MAXN << 1], to[MAXN << 1];
int pre[MAXN << 1];
int col[MAXN << 1];//1R0B
int tot = 1;
void Add(int x, int y) {
nxt[++tot] = hd[x];
hd[x] = tot;
to[tot] = y;
pre[tot] = x;
}
}e;
bool vis[MAXN];
vector<int> B;
int deg[MAXN];
int pa[MAXN], dpt[MAXN];
void dfs(int x, int fa) {//构建生成树
vis[x] = true;
pa[x] = fa;
dpt[x] = dpt[fa] + 1;
for(int i = e.hd[x]; i; i = e.nxt[i]) {
int y = e.to[i];
if(vis[y])
continue;
e.col[i] = e.col[i ^ 1] = 1;
dfs(y, x);
}
}
void Check() {
for(int i = 2; i <= m * 2; i += 2)
if(!e.col[i]) {
B.push_back(e.pre[i]);
B.push_back(e.to[i]);
deg[e.pre[i]]++, deg[e.to[i]]++;
}
if(B.size() < 6)
return ;
for(auto it : B)//用于检查是否成环
if(deg[it] < 2)
return ;
priority_queue<pair<int, int>> q;//用于找深度最大
for(auto it : B)
q.push(make_pair(dpt[it], it));
int x = q.top().second;//取出这三个点中深度最深的
int l, r;//交换 l 和 r 的颜色
for(int i = e.hd[x]; i; i = e.nxt[i]) {
int y = e.to[i];
if(e.col[i] == 0)
l = i;
if(y == pa[x])
r = i;
}
e.col[l] = e.col[l ^ 1] = 1;
e.col[r] = e.col[r ^ 1] = 0;
}
int T;
int main()
{
T = inpt();
while(T--) {
// memset(e.hd, 0, sizeof(e.hd));
// memset(e.col, 0, sizeof(e.col));
// memset(pa, 0, sizeof(pa));
// memset(dpt, 0, sizeof(dpt));
// e.tot = 1;
n = inpt(), m = inpt();
for(int i = 1; i <= m; ++i) {
int x = inpt(), y = inpt();
e.Add(x, y);
e.Add(y, x);
}
memset(deg, 0, sizeof(deg));
B.clear();
memset(vis, false, sizeof(vis));
dfs(1, 0);
Check();
for(int i = 2; i <= 2 * m; i += 2)
printf("%d", e.col[i]);
puts("");
for(int i = 1; i <= n; ++i) {
e.hd[i] = 0;
pa[i] = 0;
dpt[i] = 0;
vis[i] = false;
deg[i] = 0;
}
for(int i = 2; i <= e.tot; ++i) {
e.col[i] = 0;
}
e.tot = 1;
}
return 0;
}