CodeForces 1027D 并查集做法 最简单的做法没有之一

探讨CodeForces上一道关于在大学宿舍捕捉老鼠的算法题。通过分析老鼠的移动规律,利用并查集和图论知识,找到放置陷阱的最优策略,确保无论老鼠从哪个房间开始都能被捕捉。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:http://codeforces.com/problemset/problem/1027/D

Medicine faculty of Berland State University has just finished their admission campaign. As usual, about 80%80% of applicants are girls and majority of them are going to live in the university dormitory for the next 44 (hopefully) years.

The dormitory consists of nn rooms and a single mouse! Girls decided to set mouse traps in some rooms to get rid of the horrible monster. Setting a trap in room number ii costs cici burles. Rooms are numbered from 11 to nn.

Mouse doesn't sit in place all the time, it constantly runs. If it is in room ii in second tt then it will run to room aiai in second t+1t+1 without visiting any other rooms inbetween (i=aii=ai means that mouse won't leave room ii). It's second 00 in the start. If the mouse is in some room with a mouse trap in it, then the mouse get caught into this trap.

That would have been so easy if the girls actually knew where the mouse at. Unfortunately, that's not the case, mouse can be in any room from 11 to nn at second 00.

What it the minimal total amount of burles girls can spend to set the traps in order to guarantee that the mouse will eventually be caught no matter the room it started from?

Input

The first line contains as single integers nn (1≤n≤2⋅1051≤n≤2⋅105) — the number of rooms in the dormitory.

The second line contains nn integers c1,c2,…,cnc1,c2,…,cn (1≤ci≤1041≤ci≤104) — cici is the cost of setting the trap in room number ii.

The third line contains nn integers a1,a2,…,ana1,a2,…,an (1≤ai≤n1≤ai≤n) — aiai is the room the mouse will run to the next second after being in room ii.

Output

Print a single integer — the minimal total amount of burles girls can spend to set the traps in order to guarantee that the mouse will eventually be caught no matter the room it started from.

Examples

input

Copy

5
1 2 3 2 10
1 3 4 3 3

output

Copy

3

input

Copy

4
1 10 2 10
2 4 2 2

output

Copy

10

input

Copy

7
1 1 1 1 1 1 1
2 2 2 3 6 7 6

output

Copy

2

Note

In the first example it is enough to set mouse trap in rooms 11 and 44. If mouse starts in room 11 then it gets caught immideately. If mouse starts in any other room then it eventually comes to room 44.

In the second example it is enough to set mouse trap in room 22. If mouse starts in room 22 then it gets caught immideately. If mouse starts in any other room then it runs to room 22 in second 11.

Here are the paths of the mouse from different starts from the third example:

  • 1→2→2→…1→2→2→…;
  • 2→2→…2→2→…;
  • 3→2→2→…3→2→2→…;
  • 4→3→2→2→…4→3→2→2→…;
  • 5→6→7→6→…5→6→7→6→…;
  • 6→7→6→…6→7→6→…;
  • 7→6→7→…7→6→7→…;

So it's enough to set traps in rooms 22 and 66.

题目大意:一只老鼠从任意房间出现,可以从一个房间到另外一个制定的房间,我们可以在房间放下陷阱,每个房间的陷进有指定的value,问最小的value和。

这题看好多人都用图论做的,可惜我是弱鸡,不会那玩意,于是首先想到的是并查集。

首先来梳理一下思路:

1.每个点的出度肯定为一

2.如果所有点构成一棵树,答案就是树根的value(假设 fa[b] = a 我们就说a是b的根)

3.如果是一个森林,答案就是多个树根的value之和

上面三点是显然易见的

但是如果有强连通分量呢 假如 1->2->3->4 然后 4->1 这样就形成了一个强连通的分量,是一个环,显然我们肯定选环里面的最小值作为这个环的答案,另外如果这个环有尾巴的话,也可以一并考虑 ,比如上面不是 4->1而是4->2,1显然就是尾巴了,从1出发可以到环里面任一点,所以可以视为它在环内。然后就直接干咯。

这里我用的边带权的并查集,每个点维护从它到树根的一个最小值,当出现环的时候,把树根的权值替换成环中的最小值就行了

#include<bits/stdc++.h>
using namespace std;
int n;
const int maxn = 2e5+110;
int a[maxn],fa[maxn],b[maxn];
int get(int x){
    if(x == fa[x]) return x;
    int root = get(fa[x]);
    a[x] = min(a[x],a[fa[x]]);//利用递归维护最小值
    return fa[x] = root;
}
void merge(int x,int y){
    int v = get(y);//因为x的出度肯定为1,所以没必要get
    if(x!=v)
    fa[x] = v;//这里不要把x和v写反了,方向要确定
    a[x] =  min(a[x],a[y]);//这里y不要写成v,因为v是已经路径压缩后的点了,直接指向了根,竟然x指向y,y维护了最小,就找a[y];这是易错点
}
int main(){
    scanf("%d",&n);
    for(int i = 1; i <= n; i++)
    scanf("%d",&a[i]),fa[i] = i;
    int pos;
    for(int i = 1; i <= n; i++){
        scanf("%d",&pos);
        merge(i,pos);
    }
    int ans = 0;
    for(int i = 1; i <= n; i++)
    if(fa[i] == i)ans+=a[i];//如果是树根就加上
    printf("%d\n",ans);
    return 0;
}

还是太菜了呜呜呜~~~                                                                                                                                   

### Codeforces Round 1027 题目与题解 Codeforces Round 1027 是一场面向不同水平选手的比赛,包含多道编程问题,涉及算法、数据结构以及数学知识。以下是部分题目及其对应的题解分析。 #### A. 示例题目(假设为“A. Simple Arithmetic”) **题目描述** 给定一个整数 \( x \),判断是否可以通过某种方式将 \( x \) 分解为若干个数字的和,并满足特定条件。 **题解** 此问题可以通过简单的模拟来解决。通过检查 \( x \) 的每一位数字是否符合要求,逐步缩小范围直至找到答案[^1]。以下是一个可能的实现代码: ```cpp #include <bits/stdc++.h> using namespace std; void solve() { long long x; cin >> x; while (x) { if (x > 0 && x < 10 || x % 10 == 9) { cout << "NO\n"; return; } x /= 10; x -= 1; } cout << "YES\n"; } ``` 上述代码通过逐位检查 \( x \) 的值是否满足条件,并在不满足时直接返回“NO”。 --- #### B. 中位数调整问题(假设为“B. Median Adjustment”) **题目描述** 给定一个数组,通过删除某些元素使得中位数向左或向右移动,最终形成一段连续的合法区间。 **题解** 为了实现中位数的调整,需要枚举每一段可能的区间并判断其合法性。如果目标是左移,则优先删除右侧端点;如果是右移,则删除左侧端点[^4]。代码示例如下: ```cpp #include <bits/stdc++.h> using namespace std; int main() { int n; cin >> n; vector<int> a(n); for (auto &x : a) cin >> x; // 枚举所有可能的区间 int result = 0; for (int l = 0; l < n; ++l) { vector<int> temp; for (int r = l; r < n; ++r) { temp.push_back(a[r]); // 判断当前区间是否合法 if (is_valid(temp)) result += 1; } } cout << result; } ``` 在此代码中,`is_valid` 函数用于验证当前区间的合法性[^4]。 --- #### C. 序列构造问题(假设为“C. Sequence Construction”) **题目描述** 给定两个整数 \( n \) 和 \( k \),构造一个大小不超过 25 的非负整数序列 \( a \),使得: 1. 存在一个子序列的和为任意 \( v \neq k \) 且 \( 1 \leq v \leq n \)。 2. 不存在任何子序列的和等于 \( k \)。 **题解** 此类问题的核心在于构造一个满足条件的序列。一种常见方法是通过贪心策略逐步添加元素,同时避免生成和为 \( k \) 的子序列[^3]。以下是一个可能的实现: ```cpp #include <bits/stdc++.h> using namespace std; vector<int> construct_sequence(int n, int k) { vector<int> res; for (int i = 1; i <= n && res.size() < 25; ++i) { if (i != k) res.push_back(i); // 检查是否存在和为 k 的子序列 if (has_subsequence_sum(res, k)) return {}; } return res; } bool has_subsequence_sum(const vector<int> &a, int k) { // 使用动态规划检查是否存在子序列和为 k vector<bool> dp(k + 1, false); dp[0] = true; for (auto x : a) { for (int j = k; j >= x; --j) { dp[j] |= dp[j - x]; } } return dp[k]; } ``` 上述代码通过动态规划验证是否存在和为 \( k \) 的子序列[^3]。 --- #### D. 路径众数问题(假设为“D. Path Majority”) **题目描述** 给定一棵树,判断是否存在一条路径,使得该路径上的众数(出现次数最多的节点值)超过路径总长度的一半。 **题解** 此问题可以通过深度优先搜索(DFS)结合众数统计的方法解决。关键在于观察到如果路径上存在众数,则必然会出现两个相同的节点值相邻或间隔一个节点的情况[^2]。因此可以直接对所有节点进行简单判断即可。 ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 5; vector<int> adj[MAXN]; int values[MAXN]; bool dfs(int u, int parent, int target) { int count = 0; if (values[u] == target) count += 1; for (auto v : adj[u]) { if (v != parent) { count += dfs(v, u, target); } } return count > (adj[u].size() + 1) / 2; } bool has_majority_path(int n) { for (int i = 1; i <= n; ++i) { if (dfs(i, -1, values[i])) return true; } return false; } ``` 上述代码通过递归检查每个节点是否可能成为路径上的众数[^2]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值