CF1364D Ehab 最后的结果
1 CF1364D Ehab 最后的结果
2 题目描述
时间限制 \(1s\) | 空间限制 \(256M\)
给定一个有 \(n\) 个顶点的连通的无向图和一个整数 \(k\),你必须进行下面某个操作:
- 找出一个具有严格 \(\left\lceil\frac{k}{2}\right\rceil\) 个节点的独立集。
- 找到一个长度最多为 \(k\) 的简单环。
独立集是没有任何两个节点通过边连接这一起的定点集合。一个简单环就是不包含更小的环的环。所有给出的数据都至少能够完成上面的某一个操作。
数据范围:\(3≤𝑘≤𝑛≤10^5, 𝑛−1≤𝑚≤2\times 10^5\)。
3 题解
我们首先考虑为何两个条件必定满足至少一个。首先,如果一个图不满足第二个条件,即存在至少一个长度小于等于 \(k\) 的环,那么这个图要么没有环,要么最小环的长度大于 \(k\)。对于没有环的图来说,其一定是一棵树,而树都是二分图。所以此时整棵树一定可以通过染色将所有节点分为白色和黑色,且白色与黑色不相邻。我们只需要找到白色和黑色中最多的一个,然后去掉若干个(也可以不去)使得节点个数变为 \(\lceil \dfrac{k}{2} \rceil\)。对于长度大于 \(k\) 的环,设其长度为 \(x\),我们可以在环上隔一个点选一个点,这样我们能选出的肯定是独立集中的节点个数就是 \(\lfloor \dfrac{x}{2} \rfloor\)。此时 \(x\) 的最小值就是 \(k+1\),而 \(\lfloor \dfrac{k+1}{2} \rfloor\) 正好就是 \(\lceil \dfrac{k}{2} \rceil\)。
根据我们上面的证明,我们可以想出这道题的做法:搜出图上的任意一个简单环。这里简单环的定义就是不包含更小环的任意大小的环。找出该环后,如果这个环的长度小于等于 \(k\),那么我们直接输出这个环。否则,我们可以隔一个节点输出一个节点,一共输出有 \(\lceil \dfrac{k}{2} \rceil\) 个节点,这些点一定组成一个独立集。而如果我们找不出任意一个环,或者说这个图是一棵树,那么我们就可以利用 \(dfs\) 将整棵树进行染色,然后计算白色和黑色的节点的个数分别是多少。如果某种颜色的节点个数大于等于另外一种颜色的节点的个数,那么该种节点的个数一定 \(\ge \lceil \dfrac{n}{2} \rceil\)。由于 \(k\) 小于等于 \(n\),所以这种节点的个数一定 \(\ge \lceil \dfrac{k}{2} \rceil\)。此时任意输出 \(\lceil \dfrac{k}{2} \rceil\) 个这种节点就可以构成满足题目的最大独立集了。
在代码实现上,我们可以先找出一个环,然后用 \(tail\) 记录环中上一个节点的编号。接下来遍历所有边,如果这条边不在环上且这条边连接的两个节点在环上,那么我们可以减去这两个节点之间的两条路径上的任意一条,缩小当前这个环的长度。便利结束之后,我们得到的环就是一个简单环。其余的操作按照上述的方法模拟即可。
4 代码(空格警告):
#include <iostream>
using namespace std;
const int N = 1e5+10;
const int M = 2e5+10;
#define int long long
int n, m, k, tot, f, f2, top, st, lst, cnt;
int u[M], v[M], tail[N], co[N];
int head[N], ver[2*M], last[2*M];
int Q[N];
bool vis[N], vis2[N];
void add(int x, int y)
{
ver[++tot] = y;
last[tot] = head[x];
head[x] = tot;
}
int dfs1(int x, int fa)
{
Q[++top] = x;
if (vis[x]) return 1;
vis[x] = 1;
for (int i = head[x]; i; i = last[i])
{
int y = ver[i];
if (y == fa) continue;
f2 = dfs1(y, x);
if (f2) return f2;
}
top--;
vis[x] = 0;
return 0;
}
void dfs2(int x, int fa, int color)
{
co[x] = color;
for (int i = head[x]; i; i = last[i])
{
int y = ver[i];
if (y == fa) continue;
dfs2(y, x, color^1);
}
}
signed main()
{
cin >> n >> m >> k;
for (int i = 1; i <= m; i++)
{
cin >> u[i] >> v[i];
add(u[i], v[i]);
add(v[i], u[i]);
}
f = dfs1(1, -1);
if (!f)
{
dfs2(1, -1, 1);
k = (k+1)/2;
for (int i = 1; i <= n; i++) if (co[i]) cnt++;
if (cnt >= k)
{
cnt = 0;
cout << 1 << endl;
for (int i = 1; i <= n; i++)
{
if (co[i] && cnt == k) break;
if (co[i])
{
cnt++;
cout << i << " ";
}
}
}
else
{
cnt = 0;
cout << 1 << endl;
for (int i = 1; i <= n; i++)
{
if (!co[i] && cnt == k) break;
if (!co[i])
{
cnt++;
cout << i << " ";
}
}
}
}
else
{
st = Q[top], lst = st, top--;
vis2[st] = 1;
while (st != Q[top])
{
vis2[Q[top]] = 1;
tail[Q[top]] = lst;
lst = Q[top];
top--;
}
tail[st] = lst;
for (int i = 1; i <= m; i++)
{
if (vis2[u[i]] && vis2[v[i]] && tail[u[i]] != v[i] && tail[v[i]] != u[i])
{
for (int j = tail[u[i]]; j != v[i]; j = tail[j]) vis2[j] = 0;
tail[u[i]] = v[i];
}
}
for (int i = 1; i <= n; i++)
{
if (vis2[i])
{
st = i;
cnt++;
}
}
if (cnt <= k)
{
cout << 2 << endl << cnt << endl << st;
for (int i = tail[st]; i != st; i = tail[i]) cout << " " << i;
}
else
{
cout << 1 << endl;
k = (k+1)/2;
for (int i = st; k; i = tail[tail[i]], k--) cout << i << " ";
}
}
return 0;
}