文章目录
基本内容
并查集的存储
int fa[SIZE];
并查集的初始化
for (int i = 0; i < n; i++) {
fa[i] = i;
}
并查集的Get操作
int get(int x){
if(x==fa[x]) return x;
return fa[x]=get(fa[x]);//路径压缩
}
并查集的Merge操作
void merge(int x,int y){
fa[get(x)]=get(y);
} //
关键词: “扩展域”与“边带权”
X-Plosives
X-Plosives - UVA 1160 - Virtual Judge (vjudge.net)
思路:我们把每个元素看成顶点,则一个简单化合物就是一条边。当整个图存在环的时候,组成环的边对应的化合物是危险的,反之则是安全的。
这样,我们可以用一个并查集来维护图的连通分量集合,每次得到一个简单化合物(x,y)时检查x和y是否在同一个集合中。如果是,则拒绝,反之则接受。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Scanner;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
plosives();
}
static int[] parent = new int[100005];
public static void plosives() {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()){
for (int i = 0; i < 100005; i++) {
parent[i] = i;
}
int ans = 0;
while (true) {
int a = scanner.nextInt();
if (a == -1) {
break;
}
int b = scanner.nextInt();
a = findSet(a);
b = findSet(b);
if (a == b) {
ans++;
} else {
parent[a]=parent[b]; //省事 普通合并下
}
}
System.out.println(ans);
}
}
//带路径压缩
public static int findSet(int x) {
return parent[x] == x ? x : (parent[x] = findSet(parent[x]));
}
}
Corporative Network
CCO Preparation Test 5 P2 - Corporative Network - DMOJ ccoprep5p2 - Virtual Judge (vjudge.net)
思路:因为题目只查询结点到根结点的距离,所以每棵树除了根结点不能换之外,其他结点的位置可以任意改变,这恰好符合并查集的特点,但是需要记录附加信息。如果记录每个结点到根的距离,那么每次Ⅰ操作都要更新很多结点的信息,时间复杂度难以保证,因此考虑记下每个结点到父结点的距离为d[i],然后在路径压缩时维护这个d数组。
package Uva;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Scanner;
import java.util.StringTokenizer;
public class Main {
/**
* 快速输入类
*/
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
static StringTokenizer tokenizer = new StringTokenizer("");
/**
* 获取下一段文本
*/
static String next() throws IOException {
while (!tokenizer.hasMoreTokens()) {
tokenizer = new StringTokenizer(reader.readLine());
}
return tokenizer.nextToken();
}
static int nextInt() throws IOException {
return Integer.parseInt(next());
}
static double nextDouble() throws IOException {
return Double.parseDouble(next());
}
public static void main(String[] args) throws IOException {
corporative();
}
static int[] pa = new int[20001];
static int[] d = new int[20001];
//带路径压缩,同时维护d[x]
public static int findSet2(int x) {
if (pa[x] != x) {
int root = findSet2(pa[x]);
d[x] += d[pa[x]];
return pa[x] = root;
}
return x;
}
public static void corporative() throws IOException {
int T = nextInt();
for (int k = 0; k < T; k++) {
int n = nextInt();
for (int i = 0; i < n; i++) {
pa[i] = i;
d[i] = 0;
}
while (true) {
String next = next();
char c = next.charAt(0);
if (c == 'O') {
break;
} else if (c == 'E') {
int u =nextInt();
findSet2(u);
System.out.println(d[u]);
} else {
int u = nextInt();
int v = nextInt();
pa[u] = v;
d[u] = Math.abs(u - v) % 1000;
}
}
}
}
}
P1955 [NOI2015] 程序自动分析
[P1955 NOI2015] 程序自动分析 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
数据量大可考虑离散化,下面使用哈希表。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.StringTokenizer;
public class Main {
/**
* 快速输入类
*/
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
static StringTokenizer tokenizer = new StringTokenizer("");
/**
* 获取下一段文本
*/
static String next() throws IOException {
while (!tokenizer.hasMoreTokens()) {
tokenizer = new StringTokenizer(reader.readLine());
}
return tokenizer.nextToken();
}
static int nextInt() throws IOException {
return Integer.parseInt(next());
}
static double nextDouble() throws IOException {
return Double.parseDouble(next());
}
public static void main(String[] args) throws IOException {
NOI2015();
}
public static void NOI2015() throws IOException {
int n = nextInt();
Node[] nodes = new Node[1000000];
HashMap<Integer, Integer> map;
HashSet<Integer> set;
for (int i = 0; i < n; i++) {
int num = nextInt();
set = new HashSet<>();
map = new HashMap<>();
for (int j = 0; j < num; j++) {
nodes[j] = new Node(nextInt(), nextInt(), nextInt());
set.add(nodes[j].x);
set.add(nodes[j].y);
}
int[] arr = new int[set.size()];
Iterator<Integer> iterator = set.iterator();
int cur = 0;
while (iterator.hasNext()) {
arr[cur++] = iterator.next();
}
Arrays.sort(arr);
for (int i1 = 0; i1 < arr.length; i1++) {
map.put(arr[i1], i1);
fa[i1] = i1;
}
for (int j = 0; j < num; j++) {
if (nodes[j].e == 1) {
fa[findSet3(map.get(nodes[j].x))] = findSet3(map.get(nodes[j].y));
}
}
int flag = 0;
for (int j = 0; j < num; j++) {
if (nodes[j].e == 0 && findSet3(map.get(nodes[j].x)) == findSet3(map.get(nodes[j].y))) {
System.out.println("NO");
flag = 1;
break;
}
}
if (flag == 0) {
System.out.println("YES");
}
}
}
static int[] fa = new int[1000000];
public static class Node {
int x;
int y;
int e;
public Node(int x, int y, int e) {
this.x = x;
this.y = y;
this.e = e;
}
}
public static int findSet3(int x) {
return fa[x] == x ? x : (fa[x] = findSet3(fa[x]));
}
}
Supermarket
Supermarket - UVA 1316 - Virtual Judge (vjudge.net)
一种显而易见的贪心策略则是,优先考虑买出利润大的商品,并且对每个商品,在它过期之前尽量晚卖出——占用较晚的时间,显然对其他商品具有“决策包容性”。
于是我们可以把商品按照利润从大到小排序,并建立一个关于“天数”的并查集。起初每一天各自构成一个集合。对于每个商品,若它在d天之后过期,就在并查集中查询 d 的树根(记为r)。若r大于0,则把该商品安排在第r天卖出,合并r与r-1(令r为r-1的子节点),答案累加该商品的利润。
这个并查集实际上维护了一个数组中“位置”的占用情况。每个“位置”所在集合的代表就是从它开始往前数第一个空闲的位置(包括它本身)。当一个“位置”被占用时(某一天安排了商品),就把该“位置”在并查集中指向它前一个“位置”。利用并查集的路径压缩,就可以快速找到最晚能卖出的时间(从过期时间往前数第一个空闲的天数)。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Arrays;
public class Main {
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
static StreamTokenizer in = new StreamTokenizer(reader);
static String next() throws IOException {
in.nextToken();
return in.sval;
}
/**
* 获取数字
*/
static int nextInt() throws IOException {
in.nextToken();
return (int) in.nval;
}
static double nextDouble() throws IOException {
in.nextToken();
return in.nval;
}
public static void main(String[] args) throws IOException {
superMarket();
}
static int[] fa = new int[10001];
public static int findSet3(int x) {
return fa[x] == x ? x : (fa[x] = findSet3(fa[x]));
}
public static void superMarket() throws IOException {
int[][] arr = new int[10001][2];
while (in.nextToken() != StreamTokenizer.TT_EOF) {
int n = (int) in.nval;
for (int i = 0; i < n; i++) {
arr[i][0] = nextInt();
arr[i][1] = nextInt();
}
Arrays.sort(arr, 0, n , (o1, o2) -> {
return o2[0] - o1[0];
});
for (int i = 1; i <= 10000; i++) {
fa[i] = i;
}
int ans = 0;
for (int i = 0; i < n; i++) {
int root = findSet3(arr[i][1]);
if (root > 0) {
fa[root] = root - 1;
ans += arr[i][0];
}
}
System.out.println(ans);
}
}
}
P1196 [NOI2002] 银河英雄传说
[P1196 NOI2002] 银河英雄传说 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
public class Main {
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
static StreamTokenizer in = new StreamTokenizer(reader);
static String next() throws IOException {
in.nextToken();
return in.sval;
}
/**
* 获取数字
*/
static int nextInt() throws IOException {
in.nextToken();
return (int) in.nval;
}
static double nextDouble() throws IOException {
in.nextToken();
return in.nval;
}
public static void main(String[] args) throws IOException {
hero();
}
static int[] fa = new int[30001];
static int[] w = new int[30001];
static int[] size = new int[30001];
public static int findSet(int x) {
if (x == fa[x]) {
return x;
}
int root = findSet(fa[x]);
w[x] += w[fa[x]];
return fa[x] = root;
}
public static void merge(int x, int y) {
int root = findSet(y);
int a = findSet(x);
fa[a] = root;
w[a] = size[root];
size[root] += size[a];
}
public static void hero() throws IOException {
int n = nextInt();
for (int i = 0; i < 30001; i++) {
fa[i] = i;
size[i] = 1;
}
for (int i = 0; i < n; i++) {
String s = next();
char c = s.charAt(0);
int a = nextInt();
int b = nextInt();
if (c == 'M') {
merge(a, b);
findSet(1); //预防极端情况爆栈
} else {
if (findSet(a) != findSet(b)) {
System.out.println(-1);
} else {
System.out.println(Math.abs(w[a] - w[b]) - 1);
}
}
}
}
}
Parity Game
[P5937 CEOI1999]Parity Game - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路 :理解奇偶性传递,利用边带权
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
public class Main {
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
static StreamTokenizer in = new StreamTokenizer(reader);
static String next() throws IOException {
in.nextToken();
return in.sval;
}
/**
* 获取数字
*/
static int nextInt() throws IOException {
in.nextToken();
return (int) in.nval;
}
static double nextDouble() throws IOException {
in.nextToken();
return in.nval;
}
public static void main(String[] args) throws IOException {
parity();
}
static class Node {
int l;
int r;
int ans; //1奇0偶
public Node(int l, int r, int ans) {
this.l = l;
this.r = r;
this.ans = ans;
}
}
static int[] pa = new int[10010];
static int[] d = new int[10010]; //0 表示x和fa【x】奇偶性相同 or 1
public static int get(int x) {
if (x == pa[x]) {
return x;
}
int root = get(pa[x]);
d[x] ^= d[pa[x]];
return pa[x] = root;
}
public static void parity() throws IOException {
HashMap<Integer, Integer> map = new HashMap<>();
int len = nextInt();
int n = nextInt();
List<Node> nodes = new ArrayList<>();
HashSet<Integer> set = new HashSet<>();
for (int i = 0; i < n; i++) {
int l = nextInt();
int r = nextInt();
set.add(l - 1);
set.add(r);
int ans = 1;
String s = next();
if (s.equals("even")) {
ans = 0;
}
nodes.add(new Node(l, r, ans));
}
int[] pos = new int[set.size()];
int cur = 0;
Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()) {
pos[cur++] = iterator.next();
}
Arrays.sort(pos);
for (int i1 = 0; i1 < pos.length; i1++) {
map.put(pos[i1], i1);
pa[i1] = i1;
}
cur = 0;
while (cur < n) {
Node node = nodes.get(cur);
int x = map.get(node.l - 1);
int y = map.get(node.r);
int p = get(x), q = get(y);
if (p == q) {
if ((d[x] ^ d[y]) != node.ans) {
break;
}
} else {
pa[p] = q;
d[p] = d[x] ^ d[y] ^ node.ans; //ans=dp[p]^d[x]^d[y] 画图理解
}
cur++;
}
System.out.println(cur);
}
}
食物链
[P2024 NOI2001] 食物链 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
“扩展域”解法
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
public class Main {
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
static StreamTokenizer in = new StreamTokenizer(reader);
static String next() throws IOException {
in.nextToken();
return in.sval;
}
/**
* 获取数字
*/
static int nextInt() throws IOException {
in.nextToken();
return (int) in.nval;
}
static double nextDouble() throws IOException {
in.nextToken();
return in.nval;
}
public static void main(String[] args) throws IOException {
P2024();
}
static int[] root;
public static void P2024() throws IOException {
int n = nextInt();
int k = nextInt();
int ans = 0;
List<Item> list = new ArrayList<>();
HashSet<Integer> set = new HashSet<>();
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < k; i++) {
int type = nextInt();
int x = nextInt();
int y = nextInt();
if (x > n || y > n) {
ans++;
continue;
}
set.add(x);
set.add(y);
list.add(new Item(type, x, y));
}
Iterator<Integer> iterator = set.iterator();
int cur = 0;
while (iterator.hasNext()) {
map.put(iterator.next(), cur++);
}
root = new int[(cur + 1) * 3];
for (int i = 0; i < root.length; i++) {
root[i] = i;
}
for (int i = 0; i < list.size(); i++) {
Item item = list.get(i);
int x_self = map.get(item.x);
int x_eat = map.get(item.x) + cur;
int x_enemy = map.get(item.x) + 2 * cur;
int y_self = map.get(item.y);
int y_eat = map.get(item.y) + cur;
int y_enemy = map.get(item.y) + 2 * cur;
if (item.type == 1) {
if (get1(x_self) == get1(y_eat) || get1(x_eat) == get1(y_self)) {
ans++;
continue;
}
root[get1(x_self)] = get1(y_self);
root[get1(x_eat)] = get1(y_eat);
root[get1(x_enemy)] = get1(y_enemy);
} else {
if (get1(x_self) == get1(y_self) || get1(x_self) == get1(y_eat)) {
ans++;
continue;
}
root[get1(x_self)] = get1(y_enemy);
root[get1(x_eat)] = get1(y_self);
root[get1(x_enemy)] = get1(y_eat);
}
}
System.out.println(ans);
}
static int get1(int x) {
if (root[x] == x) {
return x;
}
return root[x] = get1(root[x]);
}
static class Item {
int type;
int x;
int y;
public Item(int type, int x, int y) {
this.type = type;
this.x = x;
this.y = y;
}
}
}