G. Penacony
G. Penacony
On Penacony, The Land of the Dreams, there exists houses and roads. There exists a road between house and for all and a road between house and house . All roads are bidirectional. However, due to the crisis on Penacony, the overseeing family has gone into debt and may not be able to maintain all roads.
There are pairs of friendships between the residents of Penacony. If the resident living in house is friends with the resident living in house , there must be a path between houses and through maintained roads.
What is the minimum number of roads that must be maintained?
Input
The first line contains () – the number of test cases.
The first line of each test case contains two integers and () – the number of houses and the number of friendships.
The next lines contain two integers and () – the resident in house is friends with the resident in house . It is guaranteed all () are distinct.
It is guaranteed the sum of and over all test cases does not exceed .
Output
For each test case, output an integer, the minimum number of roads that must be maintained.
Example
Input
7
8 3
1 8
2 7
4 5
13 4
1 13
2 12
3 11
4 10
10 2
2 3
3 4
10 4
3 8
5 10
2 10
4 10
4 1
1 3
5 2
3 5
1 4
5 2
2 5
1 3
Output
4
7
2
7
2
3
3
Note
For the first test case, the following roads must be maintained:
解题思路
对于关系 ,最优路径必然是 (记为路径 )或 (记为路径 )。这两条路径没有交集,且并集恰好是整个环。
接着有个关键的性质:最优答案必然小于 。反证法,假设最终选择了整个环的 条边,现在任选一条边删除,如果某对关系的路径包含这条边,由上述知道必然可以换成该路径的补集。因此在最优解中必然至少有一条边不会被选。为此我们可以枚举每一条边作为不被选择的边。
容易知道在删除环上的一条边后,每对关系的路径都会唯一确定。定义第 条边表示连接点 和 之间的边,那么在删除第 条边后,如果关系 满足 ,则最优路径就是路径 ,否则为路径 。显然我们可以枚举每条被删除的边,然后再枚举每对关系把路径标记出来,最后统计被标记的边的数量,取最小值,时间复杂度为 。
实际上如果从小到大枚举 ,当删除第 条边时,只有 (从路径 变路径 )和 (从路径 变路径 )的关系的路径会受到影响,因此整个过程中只会改变 次路径。如果要标记路径 ,则可以对区间 进行 ;如果要标记路径 ,则分别对区间 和 进行 。最后查询的是区间 中有标记的长度总和。【模板】扫描线的线段树可以实现该功能,线段树节点只需维护区间 中的最小值 ,以及最小值的长度总和 。查询的时候,如果 tr[1].mn == 0
,那么有标记的长度总和就是 n - tr[1].s
;否则是 n
。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5;
vector<int> l[N], r[N];
struct Node {
int l, r, mn, s, add;
}tr[N * 4];
void build(int u, int l, int r) {
tr[u] = {l, r, 0, r - l + 1, 0};
if (l != r) {
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
}
void upd(int u, int c) {
tr[u].mn += c;
tr[u].add += c;
}
void pushdown(int u) {
if (!tr[u].add) return;
upd(u << 1, tr[u].add);
upd(u << 1 | 1, tr[u].add);
tr[u].add = 0;
}
void modify(int u, int l, int r, int c) {
if (l > r) return;
if (tr[u].l >= l && tr[u].r <= r) {
upd(u, c);
}
else {
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) modify(u << 1, l, r, c);
if (r >= mid + 1) modify(u << 1 | 1, l, r, c);
tr[u].mn = min(tr[u << 1].mn, tr[u << 1 | 1].mn);
tr[u].s = 0;
if (tr[u << 1].mn == tr[u].mn) tr[u].s += tr[u << 1].s;
if (tr[u << 1 | 1].mn == tr[u].mn) tr[u].s += tr[u << 1 | 1].s;
}
}
void solve() {
int n, m;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
l[i].clear(), r[i].clear();
}
build(1, 1, n);
while (m--) {
int a, b;
scanf("%d %d", &a, &b);
l[a].push_back(b);
r[b].push_back(a);
modify(1, a, b - 1, 1);
}
int ret = n;
for (int i = 1; i <= n; i++) {
for (auto &x : l[i]) {
int a = i, b = x;
modify(1, a, b - 1, -1);
modify(1, 1, a - 1, 1);
modify(1, b, n, 1);
}
for (auto &x : r[i]) {
int a = x, b = i;
modify(1, 1, a - 1, -1);
modify(1, b, n, -1);
modify(1, a, b - 1, 1);
}
ret = min(ret, tr[1].mn ? n : n - tr[1].s);
}
printf("%d\n", ret);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
参考资料
Codeforces Round 962 (Div. 3) Editorial:https://codeforces.com/blog/entry/131528
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18327187
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2022-07-27 导弹防御系统
2022-07-27 拦截导弹