小美的朋友关系-美团2024年春招第一场笔试【技术】

题目分析:涉及到亲戚或者朋友关系,例如亲戚的亲戚是亲戚,朋友的朋友是朋友,敌人的敌人是朋友这类问题,几乎可以直接思考并查集。此题目显然也是并查集问题,但是题目的查询操作居然可以遗忘掉已有的关系再询问,这一点就很难处理。因为并查集可以并,但并没有删除操作。而且即便删除a和b的边,a和b仍然可能通过其他关系连通。解决这个问题是关键。

题解:为了解决遗忘关系后再查询的问题。此处使用了一种OI技巧,离线查询

离线查询就是把q个询问先保存起来,然后一次性处理他们,而不是按询问次序处理。

举个例子说明下,一个序列a1,a2,.......an,现在有q个查询q1,q2,q3.......,询问你前qi个数字的最大值,比如查询序列是5 3 2 7......,先得到前5个最大值,再得到前3个最大值,我们知道求前5个最大值过程中,一定是先求得前3个最大值,那么可以先把这套查询序列存起来排个序2 3  5 7 ,从前向后求最大值,求到2 3 5 7 时把答案存起来以备未来查询使用。

(1)前面m个关系先不要进行并查集建树运算,而是保存在一个set中(方便查询)。

(2)我们先把所有的q个询问都保存起来,将那些遗忘的关系都找出来,如果这个关系在前m个关系中存在,就将set中这个关系删除,不存在就是无效关系,也需标记一下(以后就不理这个关系了)。处理结束,set中保存的就是未来不会遗忘的关系,此时并查集建树。

(3)q个询问从后往前进行处理,如果是询问,就并查集中查找。如果qi是遗忘的关系,那么这个关系对于前面的q1至qi-1询问来说并没有遗忘,将其加入到并查集中。

(4)一些其他特殊的点。数据n=1e9,没法用数组存储并查集,只能用map来存储。样例很良心地给了一个需要特殊注意的点:遗忘的关系可能本就是不存在的;另外需要注意的是关系的对称性,关系(1,2)和(2,1)是相同的。

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int n,m,q,op[100005][3];/**< op数组存储q个询问 */
set<pair<int,int> > s;/**< 存储关系 */
map<int,int>f;/**< 并查集数组用map存储,因为此题目数据范围是1e9,不能用途数组 */
int findx(int x)/**< 并查集查函数 */
{
    if(f[x]==0)/**< 如果是首次调用查函数,进行f[x]赋值。 */
        f[x]=x;
    return f[x]==x?x:f[x]=findx(f[x]);
}
int main()
{
    ios::sync_with_stdio(0),cin.tie(0);
    int i,j,p,x,y;
    cin>>n>>m>>q;/**< 本题目n很大,所以不能像往常那样循环对所有f[i]赋值。 */
//    for(i=1; i<=n; i++)
//        f[i]=i;
    for(i=1; i<=m; i++)/**< 把开始的关系set存起来 */
    {
        cin>>x>>y;
        if(x>y)/**< 处理下次序,这样避免重复存储,也方便查询 */
            swap(x,y);
        s.insert({x,y});
    }
    for(i=1; i<=q; i++)
    {
        cin>>p>>x>>y;
        if(x>y)/**< 同上 */
            swap(x,y);
        op[i][0]=p,op[i][1]=x,op[i][2]=y;
        if(op[i][0]==1)
        {
            if(s.count({x,y}))/**< 1是遗忘,此处在set集合中去掉这个关系 */
                s.erase({x,y});
            else
                op[i][0]=0;/**< 如果set没有这个关系,那么标记下这个关系无效,后续不会处理 */
        }
    }
    for(auto it=s.begin(); it!=s.end(); it++)/**< 将遗忘后所有关系并查集 */
    {
        int f1=findx(it->first),f2=findx(it->second);
        if(f1!=f2)
            f[f1]=f2;
    }
    vector<string>ans;/**< 逆序存储答案 */
    for(i=q; i>=1; i--)
    {
        if(op[i][0]==1) /**< 把第i个关系加回去,对于i-1之前的询问,这个关系是有效的 */
        {
            int f1=findx(op[i][1]),f2=findx(op[i][2]);
            if(f1!=f2)
                f[f1]=f2;
        }
        else if(op[i][0]==2)
        {
            int f1=findx(op[i][1]),f2=findx(op[i][2]);
            if(f1==f2)
                ans.push_back("Yes");
            else
                ans.push_back("No");
        }
    }
    for(i=ans.size()-1; i>=0; i--)
        cout<<ans[i]<<endl;
    return 0;
}

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
2023年3月11日,美团笔试中共包含五道编程题目。以下是对每道题目的简要说明: 1. 题目一:这道题目要求解决一个数字统计的问题。可能涉及到的知识点包括数据结构、循环和条件判断等。解决问题的思路可能是使用字典等数据结构来保存统计结果,并使用循环逐个读取输入数据并进行统计。 2. 题目二:这道题目可能是一个字符串处理的问题。需要使用字符串的方法进行操作,如提取、拼接、查找和替换等。可能的解决思路包括使用正则表达式、切片和遍历等。 3. 题目三:这道题目可能涉及到算法和数据结构的知识。可能是一道涉及到数组、链表、树等数据结构的问题。解决思路可能包括遍历、递归、搜索和排序等。 4. 题目四:这道题目可能是一个动态规划的问题。需要根据给定的条件和规则,通过动态规划的方式求解问题。解决思路包括定义状态和转移方程,使用递推或记忆化搜索进行求解。 5. 题目五:这道题目可能是一个图论或网络问题。需要根据给定的图或网络结构,解决一个相关的问题。可能涉及到广度优先搜索、深度优先搜索、最短路径等知识。解决思路可能包括使用图或网络的相关算法进行求解。 以上只是对这五道编程题目的一些可能情况进行的简要描述,具体的题目内容可能会有所不同。希望这些信息能对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值