【备战春招必看】美团2025届春招第1套笔试解析 | 大厂真题通关指南

✅ 春招备战指南 ✅

💡 学习建议:

  • 先尝试独立解题(建议用时:90分钟/套)
  • 对照解析查漏补缺
  • 配套练习题库

互联网必备刷题宝典🔗

📢 美团技术岗笔试重要信息速览

⏰ 笔试时间安排

  • 常规场次:每周六交替进行
    • 上午场 10:00~11:30
    • 晚间场 19:00~20:30
  • 通知时间:每周四/五通过邮箱发送考试链接

🧩 笔试题型分布

岗位类型题目构成
算法岗选择题 + 5道编程
后端开发岗选择题 + 3道编程
前端/测试岗选择题 + 2道编程

⚙️ 考试设置要点

  • 考试平台:牛客网(ACM模式)
  • 监考要求
    • 必须开启笔记本前置摄像头
    • 禁止使用手机(需小程序锁定)
    • 允许使用本地IDE
  • 编程规范
    • 严格遵循输入输出格式
    • 注意时间复杂度控制(通常1s对应1e8次运算)

📚 笔试经验贴

(所有展示题面均已进行改编处理,保留核心考点)

本题库收录整理自:

  1. 互联网公开的笔试真题回忆版(经网友投稿)
  2. 各大技术社区公开讨论的经典题型
  3. 历年校招考生提供的解题思路

🔍 题库特点:

  • 100%真实笔试场景还原
  • 包含高频考点题型
  • 提供多语言实现参考
  • 持续更新2024届最新真题

⚠️ 注意事项:

  1. 所有题目均来自公开渠道,已进行改编脱敏处理
  2. 实际笔试可能出现题型变化,请以官方通知为准

🚀 春招备战指南

金三银四求职季即将到来!这里整理了最新美团真题及解析,助你快速掌握笔试套路。建议重点突破以下题型:

  1. 数组/字符串操作
  2. 树形结构应用
  3. 贪心/动态规划
  4. 区间合并问题

(👇 下附最新笔试真题及详细解析 👇)


真题详解(改编版)

T1

题目内容

小基很喜欢 M 和 T 这两个字母。现在小基拿到了一个仅由大写字母组成的字符串,他可以最多操作 k k k 次,每次可以修改任意一个字符。小基想知道,操作结束后最多共有多少个 M 和 T 字符?

输入描述

第一行输入两个正整数 n , k n,k n,k,代表字符串长度和操作次数。
第二行输入一个长度为 n n n 的、仅由大写字母组成的字符串。
其中, 1 ≤ k ≤ n ≤ 1 0 5 1≤k≤n≤10^5 1kn105

输出描述

输出操作结束后最多共有多少个 M 和 T 字符。

样例1

输入:

5 2
MTUAN

输出:

4

说明:修改第三个和第五个字符,形成的为 MTTAM,这样共有 4 个 M 和 T。

题解

这是一道贪心思维题,解题的关键在于理解如何最大化 M 和 T 的数量。

首先统计字符串中非 M 和 T 的字符数量,记为 c n t cnt cnt。这些字符都是可以被修改的候选。

有了操作次数 k k k 的限制,实际可修改的字符数量就是 m i n ( k , c n t ) min(k,cnt) min(k,cnt)。为什么取最小值?因为即使操作次数很多,能修改的字符数量也不能超过现有的非 M 和 T 字符的数量。

最终答案就是:原有的 M 和 T 的数量加上新增的 M 和 T 的数量,即 n − c n t + m i n ( k , c n t ) n-cnt+min(k,cnt) ncnt+min(k,cnt)

以样例为例:字符串"MTUAN"中有 3 个非 M 和 T 的字符(U、A、N),操作次数 k = 2 k=2 k=2,所以最多可以将其中 2 个字符改为 M 或 T,最终得到 4 个 M 和 T。

时间复杂度为 O ( n ) O(n) O(n),只需要遍历一次字符串统计字符数量即可。对于给定的数据范围( n ≤ 1 0 5 n≤10^5 n105)来说完全足够。

参考代码

C++:

#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, k;
    cin >> n >> k;
    string s;
    cin >> s;
    
    int cnt = 0;
    for(char c : s) {
        if(c != 'M' && c != 'T') cnt++;
    }
    
    cout << n - cnt + min(k, cnt) << endl;
    return 0;
}

Python:

n, k = map(int, input().split())
s = input()

cnt = sum(1 for c in s if c not in 'MT')
print(n - cnt + min(k, cnt))

Java:

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        String s = sc.next();
        
        int cnt = 0;
        for(char c : s.toCharArray()) {
            if(c != 'M' && c != 'T') cnt++;
        }
        
        System.out.println(n - cnt + Math.min(k, cnt));
    }
}

T2

题目内容

小基拿到了一个由正整数组成的数组,但其中有一些元素是未知的(用 0 来表示)。现在小基想知道,如果那些未知的元素在区间 [ l , r ] [l,r] [l,r] 范围内随机取值的话,数组所有元素之和的最小值和最大值分别是多少?共有 q q q 次询问。

输入描述

第一行整数 n n n q q q,表示数组的长度和询问的次数。
第二行输入 n n n 个整数 a i a_i ai,其中如果输入的 a i a_i ai 为 0,那么说明 a i a_i ai 是未知的。
接下来的 q q q 行,每行输入两个正整数 l , r l,r l,r,代表一次询问。

数据范围:

  • 1 ≤ n , q ≤ 1 0 5 1 \leq n,q \leq 10^5 1n,q105
  • 0 ≤ a i ≤ 1 0 9 0 \leq a_i \leq 10^9 0ai109
  • 1 ≤ l ≤ r ≤ 1 0 9 1 \leq l \leq r \leq 10^9 1lr109

输出描述

输出 q q q 行,每行输出两个正整数,代表所有元素之和的最小值和最大值。

样例1

输入:

3 2
3 0 2
1 1
1 2

输出:

6 6
6 7

说明:第二次询问中,最小为 1 + 2 + 3 = 6 1+2+3=6 1+2+3=6,最大为 2 + 2 + 3 = 7 2+2+3=7 2+2+3=7

题解

这是一道思维题,关键在于理解如何获取最大值和最小值。

对于每个未知数(值为 0 的位置),要使总和最小,显然应该取可选范围的最小值 l l l;要使总和最大,则应该取可选范围的最大值 r r r

解题步骤如下:

  1. 统计数组中值为 0 的元素个数,记为 z e r o _ c n t zero\_cnt zero_cnt
  2. 统计数组中所有非 0 元素的和,记为 s u m sum sum
  3. 对于每次询问:
    • 最小值 = s u m + z e r o _ c n t × l sum + zero\_cnt × l sum+zero_cnt×l
    • 最大值 = s u m + z e r o _ c n t × r sum + zero\_cnt × r sum+zero_cnt×r

注意:由于数据范围较大( 1 0 9 10^9 109),计算时需要使用 long long 类型避免溢出。

时间复杂度分析:

  • 预处理数组需要 O ( n ) O(n) O(n)
  • 每次询问只需 O ( 1 ) O(1) O(1) 的计算
  • 总时间复杂度为 O ( n + q ) O(n+q) O(n+q)
    对于给定的数据范围( n , q ≤ 1 0 5 n,q \leq 10^5 n,q105)完全可以接受。

参考代码

C++:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main() {
    int n, q;
    cin >> n >> q;
    
    ll sum = 0;
    int zero_cnt = 0;
    for(int i = 0; i < n; i++) {
        int x;
        cin >> x;
        if(x == 0) zero_cnt++;
        else sum += x;
    }
    
    while(q--) {
        ll l, r;
        cin >> l >> r;
        cout << sum + zero_cnt * l << " " << sum + zero_cnt * r << endl;
    }
    return 0;
}

Python:

n, q = map(int, input().split())
a = list(map(int, input().split()))

sum_val = sum(x for x in a if x != 0)
zero_cnt = a.count(0)

for _ in range(q):
    l, r = map(int, input().split())
    print(sum_val + zero_cnt * l, sum_val + zero_cnt * r)

Java:

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int q = sc.nextInt();
        
        long sum = 0;
        int zeroCnt = 0;
        for(int i = 0; i < n; i++) {
            int x = sc.nextInt();
            if(x == 0) zeroCnt++;
            else sum += x;
        }
        
        while(q-- > 0) {
            long l = sc.nextLong();
            long r = sc.nextLong();
            System.out.println((sum + zeroCnt * l) + " " + (sum + zeroCnt * r));
        }
    }
}

T3

题目内容

小基拿到了一个 n × n n×n n×n 的矩阵,其中每个元素是 0 或者 1。小基认为一个矩形区域是完美的,当且仅当该区域内 0 的数量等于 1 的数量。现在,小基希望知道有多少个 i × i i×i i×i 的完美矩形区域。需要回答 1 ≤ i ≤ n 1≤i≤n 1in 的所有答案。

输入描述

第一行输入一个正整数 n n n,代表矩阵大小。
接下来的 n n n 行,每行输入一个长度为 n n n 的 01 串,用来表示矩阵。
其中, 1 ≤ n ≤ 200 1≤n≤200 1n200

输出描述

输出 n n n 行,第 i i i 行输出 i × i i×i i×i 的完美矩形区域的数量。

样例1

输入:

4
1010
0101
1100
0011

输出:

0
7
0
1

题解

这道题目可以使用二维前缀和来解决。

核心思路:

  1. 先预处理出二维前缀和数组,用于快速计算任意矩形区域内 1 的个数。
  2. 枚举所有可能的正方形区域,检查是否完美。
  3. 一个区域要完美,需要区域内 0 和 1 的数量相等,即 1 的数量应该是区域大小的一半。

具体实现步骤:

  1. 构建前缀和数组 s u m [ i ] [ j ] sum[i][j] sum[i][j],表示从 ( 1 , 1 ) (1,1) (1,1) ( i , j ) (i,j) (i,j) 的矩形区域内 1 的个数。
  2. 对于每个边长 l e n len len,枚举左上角坐标 ( x , y ) (x,y) (x,y)
  3. 计算对应的右下角坐标 ( x + l e n − 1 , y + l e n − 1 ) (x+len-1,y+len-1) (x+len1,y+len1)
  4. 使用前缀和计算该区域内 1 的个数,判断是否等于区域大小的一半。

时间复杂度分析:

  • 预处理前缀和需要 O ( n 2 ) O(n^2) O(n2)
  • 枚举所有可能的正方形需要 O ( n 3 ) O(n^3) O(n3)
  • 总时间复杂度为 O ( n 3 ) O(n^3) O(n3)
    n ≤ 200 n≤200 n200 的数据范围内可以通过。

参考代码

C++:

#include <bits/stdc++.h>
using namespace std;

const int N = 205;
int sum[N][N], n;
char g[N][N];

int get_sum(int x1, int y1, int x2, int y2) {
    return sum[x2][y2] - sum[x1-1][y2] - sum[x2][y1-1] + sum[x1-1][y1-1];
}

int main() {
    cin >> n;
    // 读入矩阵并构建前缀和数组
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            cin >> g[i][j];
            sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + (g[i][j] == '1');
        }
    }
    
    // 枚举所有可能的正方形大小
    for(int len = 1; len <= n; len++) {
        int ans = 0;
        // 枚举左上角坐标
        for(int i = 1; i + len - 1 <= n; i++) {
            for(int j = 1; j + len - 1 <= n; j++) {
                int ones = get_sum(i, j, i + len - 1, j + len - 1);
                if(ones * 2 == len * len) ans++;
            }
        }
        cout << ans << endl;
    }
    return 0;
}

Python:

n = int(input())
grid = ['0' * (n+1)] + ['0' + input() for _ in range(n)]

# 构建前缀和数组
sum_matrix = [[0] * (n+1) for _ in range(n+1)]
for i in range(1, n+1):
    for j in range(1, n+1):
        sum_matrix[i][j] = (sum_matrix[i-1][j] + 
                           sum_matrix[i][j-1] - 
                           sum_matrix[i-1][j-1] + 
                           int(grid[i][j] == '1'))

def get_sum(x1, y1, x2, y2):
    return (sum_matrix[x2][y2] - 
            sum_matrix[x1-1][y2] - 
            sum_matrix[x2][y1-1] + 
            sum_matrix[x1-1][y1-1])

# 枚举所有可能的正方形大小
for length in range(1, n+1):
    ans = 0
    # 枚举左上角坐标
    for i in range(1, n-length+2):
        for j in range(1, n-length+2):
            ones = get_sum(i, j, i+length-1, j+length-1)
            if ones * 2 == length * length:
                ans += 1
    print(ans)

Java:

import java.util.*;

public class Main {
    static int N = 205;
    static int[][] sum = new int[N][N];
    static char[][] g = new char[N][N];
    static int n;
    
    static int getSum(int x1, int y1, int x2, int y2) {
        return sum[x2][y2] - sum[x1-1][y2] - sum[x2][y1-1] + sum[x1-1][y1-1];
    }
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        sc.nextLine(); // 消耗换行符
        
        // 读入矩阵并构建前缀和数组
        for(int i = 1; i <= n; i++) {
            String line = sc.nextLine();
            for(int j = 1; j <= n; j++) {
                g[i][j] = line.charAt(j-1);
                sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + 
                           (g[i][j] == '1' ? 1 : 0);
            }
        }
        
        // 枚举所有可能的正方形大小
        for(int len = 1; len <= n; len++) {
            int ans = 0;
            // 枚举左上角坐标
            for(int i = 1; i + len - 1 <= n; i++) {
                for(int j = 1; j + len - 1 <= n; j++) {
                    int ones = getSum(i, j, i + len - 1, j + len - 1);
                    if(ones * 2 == len * len) ans++;
                }
            }
            System.out.println(ans);
        }
    }
}

T4

题目内容

小基拿到了一个大小为 n n n 的数组,他希望删除一个区间后,使得剩余所有元素的乘积末尾至少有 k k k 个 0。小基想知道,一共有多少种不同的删除方案?

输入描述

第一行输入两个正整数 n , k n,k n,k
第二行输入 n n n 个正整数 a i a_i ai,代表小基拿到的数组。
其中, 1 ≤ n , k ≤ 1 0 5 1≤n,k≤10^5 1n,k105 1 ≤ a i ≤ 1 0 9 1≤a_i≤10^9 1ai109

输出描述

输出一个整数,代表删除的方案数。

样例1

输入:

5 2
2 5 3 4 20

输出:

4

题解

这道题目使用双指针算法求解,关键思路如下:

  1. 统计整个数组中每个数的因子 2 和因子 5 的个数。
  2. 使用双指针维护删除区间,计算剩余数字中因子的总数。
  3. 当某个区间删除后不满足条件时,需要缩小删除区间。
  4. 当某个区间满足条件时,可以计算以当前右端点结尾的所有合法方案。

时间复杂度分析:

  • 预处理因子个数: O ( n log ⁡ M ) O(n\log M) O(nlogM)
  • 双指针遍历: O ( n ) O(n) O(n)
    总体复杂度: O ( n log ⁡ M ) O(n\log M) O(nlogM)

参考代码

C++:

#include <bits/stdc++.h>
using namespace std;
#define int long long

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int l = 0, r = 0;
    int n, k;
    cin >> n >> k;
    
    vector<int> a(n);
    vector<array<int, 2>> cnt(n);
    
    // 统计每个数的因子2和5的个数
    for(int i = 0; i < n; i++) {
        cin >> a[i];
        int x = a[i];
        while(x % 2 == 0) x >>= 1, l++, cnt[i][0]++;
        while(x % 5 == 0) x /= 5, r++, cnt[i][1]++;
    }
    
    int res = 0;
    array<int, 2> c{l, r};  // 记录当前未被删除的数中因子2和5的总数
    int j = 0;  // 左指针
    
    // 枚举右指针
    for(int i = 0; i < n; i++) {
        // 删除当前位置的数
        for(int p = 0; p < 2; p++)
            c[p] -= cnt[i][p];
            
        // 当不满足条件时,缩小删除区间
        while(j <= i && *min_element(c.begin(), c.end()) < k) {
            for(int p = 0; p < 2; p++)
                c[p] += cnt[j][p];
            j++;
        }
        
        // 计算合法方案数
        res += i - j + 1;
    }
    
    cout << res << "\n";
    return 0;
}

Python:

n, k = map(int, input().split())
a = list(map(int, input().split()))

# 统计因子个数
cnt = [[0, 0] for _ in range(n)]
l = r = 0

for i in range(n):
    x = a[i]
    while x % 2 == 0:
        x //= 2
        l += 1
        cnt[i][0] += 1
    while x % 5 == 0:
        x //= 5
        r += 1
        cnt[i][1] += 1

res = 0
c = [l, r]  # 记录当前未被删除的数中因子2和5的总数
j = 0  # 左指针

# 枚举右指针
for i in range(n):
    # 删除当前位置的数
    for p in range(2):
        c[p] -= cnt[i][p]
    
    # 当不满足条件时,缩小删除区间
    while j <= i and min(c) < k:
        for p in range(2):
            c[p] += cnt[j][p]
        j += 1
    
    # 计算合法方案数
    res += i - j + 1

print(res)

Java:

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        
        long[] a = new long[n];
        int[][] cnt = new int[n][2];
        long l = 0, r = 0;
        
        // 统计因子个数
        for(int i = 0; i < n; i++) {
            a[i] = sc.nextLong();
            long x = a[i];
            while(x % 2 == 0) {
                x /= 2;
                l++;
                cnt[i][0]++;
            }
            while(x % 5 == 0) {
                x /= 5;
                r++;
                cnt[i][1]++;
            }
        }
        
        long res = 0;
        long[] c = new long[]{l, r};
        int j = 0;
        
        // 枚举右指针
        for(int i = 0; i < n; i++) {
            // 删除当前位置的数
            for(int p = 0; p < 2; p++)
                c[p] -= cnt[i][p];
            
            // 当不满足条件时,缩小删除区间
            while(j <= i && Math.min(c[0], c[1]) < k) {
                for(int p = 0; p < 2; p++)
                    c[p] += cnt[j][p];
                j++;
            }
            
            // 计算合法方案数
            res += i - j + 1;
        }
        
        System.out.println(res);
    }
}

T5

题目内容

小德认为,在人际交往中,随着时间的流逝,朋友的关系会逐渐淡化,最终朋友关系会被遗忘。现在初始有一些朋友关系,存在一些事件会导致两个人淡忘了他们的朋友关系。小德想知道某一时刻中,某两人是否可以通过朋友介绍互相认识。

事件共有两种:

  1. 1 小基 小柯:代表编号 u u u 的人和编号 v v v 的人淡忘了他们的朋友关系。
  2. 2 小基 小兰:代表小德查询编号 u u u 的人和编号 v v v 的人是否能通过朋友介绍互相认识。

注:介绍可以有多层,比如小基把小柯介绍给小兰,然后小兰再把小基介绍给小兰,这样小基和小兰就认识了。

输入描述

第一行输入三个正整数 n n n, m m m, q q q,代表总人数,初始的朋友关系数量,发生的事件数量。

接下来的 m m m 行,每行输入两个正整数 u u u, v v v,代表初始编号 u u u 的人和编号 v v v 的人是朋友关系。

接下来的 q q q 行,每行输入三个正整数 o p op op, u u u, v v v,含义如题目描述所述。

  • 1 ≤ n ≤ 1 0 9 1 \le n \le 10^9 1n109
  • 1 ≤ m , q ≤ 1 0 5 1 \le m, q \le 10^5 1m,q105
  • 1 ≤ u , v ≤ n 1 \le u, v \le n 1u,vn
  • 1 ≤ o p ≤ 2 1 \le op \le 2 1op2

输出描述

对于每次 2 2 2 号操作,输出一行字符代表查询的答案。

如果编号 u u u 的人和编号 v v v 的人能通过朋友介绍互相认识,则输出 "Yes"。否则输出 "No"

样例1

输入

5 3 5
1 2
2 3
4 5
1 1 5
2 1 3
2 1 4
1 1 2
2 1 3

输出

Yes
No
No

题解

这个问题的核心在于管理和查询动态的社交网络关系。初始时,某些人之间有直接的朋友关系,但随着事件的发生,这些关系可能会被删除。小德需要在不同的时间点查询两个人是否通过现有的朋友关系链条互相认识。

为了高效地处理这种动态的连接和查询,可以使用**并查集(Union-Find)**数据结构。并查集能够快速地合并两个集合以及判断两个元素是否属于同一集合。然而,并查集本身不支持高效的删除操作。因此,采用逆序处理的方法来简化问题。

解题步骤如下:

  1. 读取输入并记录所有相关节点:首先,读取所有初始的朋友关系和事件,记录所有涉及的人的编号。

  2. 映射节点编号:由于 n n n 可能高达 1 0 9 10^9 109,而实际涉及的节点数不超过 2 × ( m + q ) 2 \times (m + q) 2×(m+q),可以将每个唯一的节点编号映射到一个连续的小编号,便于后续处理。

  3. 标记被删除的边:遍历所有事件,记录哪些边在事件中被删除。这些边在初始阶段不应被加入到并查集中。

  4. 初始化并查集:将所有未被删除的初始边加入并查集中,建立初始的连接关系。

  5. 逆序处理事件

    • 从最后一个事件开始向前处理。
    • 对于每个事件:
      • 如果是查询操作(类型 2 2 2),使用并查集判断两个人是否属于同一集合,并记录结果。
      • 如果是删除操作(类型 1 1 1),在逆序处理中相当于添加这条边到并查集中。
  6. 输出结果:由于事件是逆序处理的,最后需要将记录的查询结果反转过来,按照原始顺序输出。

关键点:

  • 并查集:用于高效管理和查询连接关系。
  • 逆序处理:将删除操作转化为添加操作,避免直接处理删除的复杂性。
  • 离线处理:先记录所有事件,再统一处理,确保查询时的集合状态正确。

时间复杂度分析:

并查集的每次操作接近常数时间,因此整体时间复杂度为 O ( ( m + q ) ⋅ α ( n ) ) O((m + q) \cdot \alpha(n)) O((m+q)α(n)),其中 α ( n ) \alpha(n) α(n) 是阿克曼函数的反函数,增长极其缓慢,可以认为是常数。对于给定的数据范围,这种复杂度是可以接受的。

三语言参考代码

  • Cpp

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    
    struct UnionFind {
        vector<int> parent;
        vector<int> rank;
        UnionFind(int size) : parent(size + 1), rank(size + 1, 1) {
            for(int i = 0; i <= size; ++i) parent[i] = i;
        }
        int find_set(int x) {
            if(parent[x] != x)
                parent[x] = find_set(parent[x]);
            return parent[x];
        }
        void union_set(int x, int y) {
            int fx = find_set(x);
            int fy = find_set(y);
            if(fx == fy) return;
            if(rank[fx] < rank[fy]) {
                parent[fx] = fy;
            }
            else {
                parent[fy] = fx;
                if(rank[fx] == rank[fy]) rank[fx]++;
            }
        }
    };
    
    int main(){
        ios::sync_with_stdio(false);
        cin.tie(0);
        int n, m, q;
        cin >> n >> m >> q;
        vector<pair<int, int>> initial_edges(m);
        for(auto &p : initial_edges){
            cin >> p.first >> p.second;
        }
        struct Event {
            int op;
            int u;
            int v;
        };
        vector<Event> events(q);
        set<pair<int, int>> removed;
        for(int i = 0; i < q; ++i){
            cin >> events[i].op >> events[i].u >> events[i].v;
            if(events[i].op == 1){
                int u = events[i].u;
                int v = events[i].v;
                if(u > v) swap(u, v);
                removed.emplace(make_pair(u, v));
            }
        }
        vector<int> nodes;
        for(auto &p : initial_edges){
            nodes.push_back(p.first);
            nodes.push_back(p.second);
        }
        for(auto &e : events){
            nodes.push_back(e.u);
            nodes.push_back(e.v);
        }
        sort(nodes.begin(), nodes.end());
        nodes.erase(unique(nodes.begin(), nodes.end()), nodes.end());
        unordered_map<int, int> mapper;
        int idx = 1;
        for(auto &x : nodes){
            mapper[x] = idx++;
        }
        int size = idx;
        UnionFind uf(size);
        for(auto &p : initial_edges){
            int u = p.first;
            int v = p.second;
            if(u > v) swap(u, v);
            if(removed.find(make_pair(u, v)) == removed.end()){
                uf.union_set(mapper[u], mapper[v]);
            }
        }
        vector<string> answers;
        for(int i = q-1; i >=0; --i){
            if(events[i].op == 2){
                int u = events[i].u;
                int v = events[i].v;
                if(mapper.find(u) == mapper.end() || mapper.find(v) == mapper.end()){
                    answers.emplace_back("No");
                }
                else{
                    if(uf.find_set(mapper[u]) == uf.find_set(mapper[v])){
                        answers.emplace_back("Yes");
                    }
                    else{
                        answers.emplace_back("No");
                    }
                }
            }
            else if(events[i].op == 1){
                int u = events[i].u;
                int v = events[i].v;
                uf.union_set(mapper[u], mapper[v]);
            }
        }
        for(int i = answers.size()-1; i >=0; --i){
            cout << answers[i] << "\n";
        }
    }
    
    
  • Python

    import sys
    sys.setrecursionlimit(1 << 25)
    
    class UnionFind:
        def __init__(self, size):
            self.parent = list(range(size + 1))
            self.rank = [1] * (size +1)
        
        def find_set(self, x):
            if self.parent[x] != x:
                self.parent[x] = self.find_set(self.parent[x])
            return self.parent[x]
        
        def union_set(self, x, y):
            fx = self.find_set(x)
            fy = self.find_set(y)
            if fx == fy:
                return
            if self.rank[fx] < self.rank[fy]:
                self.parent[fx] = fy
            else:
                self.parent[fy] = fx
                if self.rank[fx] == self.rank[fy]:
                    self.rank[fx] +=1
    
    def main():
        import sys
        input = sys.stdin.read
        data = input().split()
        ptr =0
        n = int(data[ptr]); ptr+=1
        m = int(data[ptr]); ptr+=1
        q = int(data[ptr]); ptr+=1
        initial_edges = []
        for _ in range(m):
            u = int(data[ptr]); ptr+=1
            v = int(data[ptr]); ptr+=1
            initial_edges.append( (u, v) )
        events = []
        removed = set()
        for _ in range(q):
            op = int(data[ptr]); ptr+=1
            u = int(data[ptr]); ptr+=1
            v = int(data[ptr]); ptr+=1
            events.append( (op, u, v) )
            if op ==1:
                if u > v:
                    u, v = v, u
                removed.add( (u, v) )
        nodes = set()
        for u, v in initial_edges:
            nodes.add(u)
            nodes.add(v)
        for event in events:
            _, u, v = event
            nodes.add(u)
            nodes.add(v)
        mapper = {}
        sorted_nodes = sorted(list(nodes))
        for idx, x in enumerate(sorted_nodes,1):
            mapper[x] = idx
        uf = UnionFind(len(sorted_nodes))
        for u, v in initial_edges:
            a, b = u, v
            if a > b:
                a, b = b, a
            if (a, b) not in removed:
                uf.union_set(mapper[a], mapper[b])
        answers = []
        for event in reversed(events):
            op, u, v = event
            if op ==2:
                if u not in mapper or v not in mapper:
                    answers.append("No")
                else:
                    if uf.find_set(mapper[u]) == uf.find_set(mapper[v]):
                        answers.append("Yes")
                    else:
                        answers.append("No")
            elif op ==1:
                uf.union_set(mapper[u], mapper[v])
        for ans in reversed(answers):
            print(ans)
    
    if __name__ == "__main__":
        main()
    
    
  • Java

    import java.io.*;
    import java.util.*;
    
    public class Main {
        static class UnionFind {
            int[] parent;
            int[] rank;
    
            public UnionFind(int size){
                parent = new int[size +1];
                rank = new int[size +1];
                for(int i=0;i<=size;i++) parent[i] = i;
                Arrays.fill(rank, 1);
            }
    
            public int find_set(int x){
                if(parent[x] != x){
                    parent[x] = find_set(parent[x]);
                }
                return parent[x];
            }
    
            public void union_set(int x, int y){
                int fx = find_set(x);
                int fy = find_set(y);
                if(fx == fy) return;
                if(rank[fx] < rank[fy]){
                    parent[fx] = fy;
                }
                else{
                    parent[fy] = fx;
                    if(rank[fx] == rank[fy]) rank[fx]++;
                }
            }
        }
    
        public static void main(String[] args) throws IOException{
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            String[] first = br.readLine().split(" ");
            int n = Integer.parseInt(first[0]);
            int m = Integer.parseInt(first[1]);
            int q = Integer.parseInt(first[2]);
            List<int[]> initial_edges = new ArrayList<>();
            for(int i=0;i<m;i++){
                String[] parts = br.readLine().split(" ");
                int u = Integer.parseInt(parts[0]);
                int v = Integer.parseInt(parts[1]);
                initial_edges.add(new int[]{u, v});
            }
            class Event {
                int op;
                int u;
                int v;
                Event(int op, int u, int v){
                    this.op = op;
                    this.u = u;
                    this.v = v;
                }
            }
            List<Event> events = new ArrayList<>();
            Set<String> removed = new HashSet<>();
            for(int i=0;i<q;i++){
                String[] parts = br.readLine().split(" ");
                int op = Integer.parseInt(parts[0]);
                int u = Integer.parseInt(parts[1]);
                int v = Integer.parseInt(parts[2]);
                events.add(new Event(op, u, v));
                if(op ==1){
                    if(u > v){
                        int temp = u;
                        u = v;
                        v = temp;
                    }
                    removed.add(u + "," + v);
                }
            }
            Set<Integer> nodeSet = new HashSet<>();
            for(int[] edge : initial_edges){
                nodeSet.add(edge[0]);
                nodeSet.add(edge[1]);
            }
            for(Event e : events){
                nodeSet.add(e.u);
                nodeSet.add(e.v);
            }
            List<Integer> sorted_nodes = new ArrayList<>(nodeSet);
            Collections.sort(sorted_nodes);
            Map<Integer, Integer> mapper = new HashMap<>();
            for(int i=0;i<sorted_nodes.size();i++){
                mapper.put(sorted_nodes.get(i), i+1);
            }
            UnionFind uf = new UnionFind(sorted_nodes.size());
            for(int[] edge : initial_edges){
                int u = edge[0];
                int v = edge[1];
                if(u > v){
                    int temp = u;
                    u = v;
                    v = temp;
                }
                if(!removed.contains(u + "," + v)){
                    uf.union_set(mapper.get(u), mapper.get(v));
                }
            }
            List<String> answers = new ArrayList<>();
            Collections.reverse(events);
            for(Event e : events){
                if(e.op == 2){
                    if(!mapper.containsKey(e.u) || !mapper.containsKey(e.v)){
                        answers.add("No");
                    }
                    else{
                        if(uf.find_set(mapper.get(e.u)) == uf.find_set(mapper.get(e.v))){
                            answers.add("Yes");
                        }
                        else{
                            answers.add("No");
                        }
                    }
                }
                else if(e.op ==1){
                    uf.union_set(mapper.get(e.u), mapper.get(e.v));
                }
            }
            Collections.reverse(answers);
            for(String ans : answers){
                System.out.println(ans);
            }
        }
    }
    
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值