AtCoder Beginner Contest 279 F BOX 并查集 (大意失荆州

前言

赛时一直RE,思路很清晰,不知道RE哪里。。qwq
赛后开断点发现,map的大小不变,
最后发现是一个if条件写错了,寄。

不知道为什么会想起 银河英雄传说

题意:

初始n个盒子,盒子 i i i放着编号为 i i i的小球
进行q次操作

  • op1:将y盒子的小球移到x盒子中
  • op2:将第k个球,加入到x盒子中
  • op3:询问球x在哪个盒子中

思路:

我们看到op1,联想到集合的合并,于时想到并查集的操作在这里插入图片描述
图中绿色的点代表盒子。

  1. 经过 多个op1,op1,op1…我们可以用并查集把这些合并到盒子2
  2. 此时,突然有个 o p 1   1   2 op_1 \ 1 \ 2 op1 1 2, 那么我们 m e r g e ( b o x [ 1 ] , b o x [ 2 ] ) merge(box[1],box[2]) merge(box[1],box[2])
merge(int x, int y){
	int fx = find(x), fy = find(y);
	p[fy] = fx;//根据题意合并
}
  1. 误区:此时突然来个 o p 2   2 op_2 \ 2 op2 2

假如新球,连到原来的盒子p[k] = find(box[2]) ,最后会使得新球在盒子1其实新球在盒子2

因为p[box[2]] == p[box[1]]

做法

  • 对于每个盒子和点都编号
  • 每次操作1后,把 y y y盒子删除,新建一个 y ‘ y ` y
box[y] = ++newBox;

What’s more

看了SSRS,好像可以用启发式合并,一开始我也是想用,但后面发现新开点,就可以解决了。

Java

package com.hgs.atcoder.abc.contest279.f;

/**
 * @author youtsuha
 * @version 1.0
 * Create by 2022/11/26 19:43
 */
import java.util.*;
import java.io.*;
public class Main{
    static FastScanner cin;
    static PrintWriter cout;

    private static void init()throws IOException {
        cin = new FastScanner(System.in);
        cout = new PrintWriter(System.out);
    }

    private static void close(){
        cout.close();
    }
    static int[] p;
    static int find(int x){
        if(x == p[x]) {
            return p[x];
        }
        return p[x] = find(p[x]);
    }
    private static void sol()throws IOException {
        int n = cin.nextInt(), m = cin.nextInt();
        int tot = n  + m;
        p = new int[tot*2 + 1];
        Map<Integer,Integer> curBox = new HashMap<>();
        Map<Integer,Integer> boxPre = new HashMap<>();
        int newBox = tot;
        //[1,tot] balls
        //[tot+1,tot*2] boxs
        for(int i = 1; i <= tot*2; i ++ ) {
            p[i] = i;
            if(i > tot && i <= tot + n){
                newBox++;
                curBox.put(i - tot,i);
                boxPre.put(i,i-tot);
            }else if(i <= n){
                p[i] = i + tot;
            }
        }
        int k = n;
        for(int i = 1; i <= m; i ++ ) {
            int op = cin.nextInt(), x = cin.nextInt();
            if(op == 1){
                int y = cin.nextInt();
                //y-->x
                int preY = curBox.get(y);
                p[preY] = p[curBox.get(x)];

                boxPre.put(++newBox,y);
                curBox.put(y,newBox);
            }else if(op == 2){
                k++;
                p[k] = curBox.get(x);
            }else {
                cout.println(boxPre.get(find(p[x])));
            }
        }

    }
    public static void main(String[] args) throws IOException {
        init();
        sol();
        close();
    }
}
class FastScanner {
    BufferedReader br;
    StringTokenizer st = new StringTokenizer("");

    public FastScanner(InputStream s) {
        br = new BufferedReader(new InputStreamReader(s));
    }

    public FastScanner(String s) throws FileNotFoundException {
        br = new BufferedReader(new FileReader(new File(s)));
    }

    public String next() throws IOException {
        while (!st.hasMoreTokens()){
            try {
                st = new StringTokenizer(br.readLine());
            } catch (IOException e) { e.printStackTrace(); }
        }
        return st.nextToken();
    }

    public int nextInt() throws IOException {
        return Integer.parseInt(next());
    }

    public long nextLong() throws IOException {
        return Long.parseLong(next());
    }

    public double nextDouble() throws IOException {
        return Double.parseDouble(next());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值