BUCT-2021年ACM竞赛班训练(一)2021.3.25-问题 A: 大佬的高级IDLE-题解

59 篇文章 23 订阅


大佬的高级IDLE

传送门

时间限制:2秒
空间限制:128M

题目描述

某厉害的大佬,自己写了个高级的IDLE。它能帮助这位大佬检测一些变量的赋值是否符合逻辑。
大佬编程时,程序中出现了一些变量(x1, x2, x3, ··· ),并且有 n 个形如 xi = xj 或 xi ≠ xj 的变量相等/不想等的约束条件。
这个IDLE能够自动判断是否可以分别给每一个变量赋予一个恰当的值,使得这 n 个条件能够同时被满足。
例如,一次编程时,约束条件为:x1 = x2, x2 = x3, x3 = x4, x1 ≠ x4
显然这些条件无法同时被满足,因此IDLE会报错"CANNOT"。否则会提示"OK"。
现给定一些约束条件,请分别判断IDLE会提示什么。

输入描述

第1行包含1个正整数N,表示大佬的N个代码。注意这些代码之间是相互独立的。
对于每个代码,包含若干行:
第1行包含1个正整数n,表示该代码中出现的约束条件的个数。
接下来n行,每行包括3个整数 u, v, op,描述1个相等/不等的约束条件,相邻整数之间用单个空格隔开。若 op = 1 ,则该约束条件为 xu = xv ;若 op = 0 ,则该约束条件为 xu ≠ xv


数据范围:
1 ≤ ∑n ≤ 100000
1 ≤ u ,v ≤ 1000000000

输出描述

包括N行,代表大佬的N个代码。
第i行输出一个字符串"CANNOT"或"OK",(不包含引号,字母全大写),"CANNOT"表示给定的n个约束条件不能被同时满足,"OK"表示可以被同时满足

示例1

输入
2
2
1 2 1
1 2 0
2
1 2 1
2 1 1
输出
CANNOT
OK
说明

在第一个问题中,约束条件为:x 1 _1 1=x 2 _2 2,x 1 _1 1≠x 2 _2 2。这两个约束条件互相矛盾,因此不可被同时满足。

在第二个问题中,约束条件为:x 1 _1 1=x 2 _2 2,x 1 _1 1=x 2 _2 2。这两个约束条件是等价的,可以被同时满足。


题目分析

显然,我们要使用并查集。
对于x u _u u=x v _v v,u和v在同一个集合中。
对于x u _u u≠x v _v v,u和v不能在同一个集合中。
因题目数据范围是1e9,故需要进行离散化。

解题思路

先处理相等的,再处理不想等的。
对于输入的u和v

  • 如果x u _u u和x v _v v要相等,就把它们并入到一个集合中。
  • 如果x u _u u和x v _v v要不相等,它们就不应该在同一个集合中
    • 如果在,那就输出"CANNOT"并结束
    • 如果所有的不想等判断完毕,都满足条件,就输出"OK"

并查集离散化

l和r都存入Rank
sort
unique去重
离散化后分别对应
假如要联合
l r
1 2
5 6
7 10
1 9
Rank:
1
2
5
6
7
10
1
9
1
1
2
5
6
7
10
1
2
5
6
7
10
0
1
2
3
4
5

AC代码

#include <bits/stdc++.h>
using namespace std;
#define SIZE 100010 
struct connect 
{
    int u, v, op;
} con[SIZE];
bool operator<(const connect &a, const connect &b)
{
    return a.op > b.op;
} //对<的定义:操作数为1(相等)的排在前面先处理
int father[SIZE]; 
int getFather(int n)
{
    return (n == father[n]) ? (n) : (father[n] = getFather(father[n]));
}
void Union(int u, int v) //连接两个集合
{
    int fu = getFather(u);
    int fv = getFather(v);
    if (fu != fv)
        father[fu] = fv;
}
int Rank[SIZE * 2]; //需要离散化
int main()
{
    int N;
    cin >> N;
    while (N--) //N组测试样例
    {
        int n; //每组有n对
        scanf("%d", &n);
        for (int i = 0; i < n; i++)
        {
            scanf("%d%d%d", &con[i].u, &con[i].v, &con[i].op);
            Rank[i * 2] = con[i].u; //把l和r放入Rank中
            Rank[i * 2 + 1] = con[i].v;
        }
        sort(Rank, Rank + n * 2); //排序
        int allNum = unique(Rank, Rank + 2 * n) - Rank; //去重后,共有几种不同的元素
        for (int i = 0; i < n; i++)
        {
            con[i].u = lower_bound(Rank, Rank + allNum, con[i].u) - Rank; //对于每一个数,离散化后对应的数
            con[i].v = lower_bound(Rank, Rank + allNum, con[i].v) - Rank;
        }
        sort(con, con + n); //把要连接的放到前面
        for (int i = 0; i < allNum; i++) //初始化father等于自己
            father[i] = i;
        for (int i = 0; i < n; i++)
        {
            int u = getFather(con[i].u);//找到father
            int v = getFather(con[i].v);
            if (con[i].op == 1) //如果是要合并的
                Union(u, v); //就合并两个集合
            else if (u == v) //不能在同一个集合中,但是却在同一个集合中
            {
                puts("CANNOT"); //输出CANNOT
                goto loop; //结束,跳到loop处 ---->---+
            }//                                      |                  
        }//                                          |
        puts("OK");//没有不符合条件的,输出OK          |
    loop:; //跳到这里 <-------<----------<-----------+
    }
    return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tisfy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值