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

18 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

@TOC


大佬的高级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

说明

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

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


题目分析

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

解题思路

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

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

并查集离散化

graph LR
A[假如要联合<br>l r<br>1 2<br>5 6<br>7 10<br>1 9<br>]
A-->|l和r都存入Rank|B[Rank:<br>1<br>2<br>5<br>6<br>7<br>10<br>1<br>9<br>]
B-->|sort|C[1<br>1<br>2<br>5<br>6<br>7<br>10]
C-->|unique去重|D[1<br>2<br>5<br>6<br>7<br>10]
D-->|离散化后分别对应|E[0<br>1<br>2<br>3<br>4<br>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;
}

同步发文于我的CSDN