洛谷题单指南-集合-P1955 [NOI2015] 程序自动分析

原题链接:https://www.luogu.com.cn/problem/P1955

题意解读:要判断约束条件是否可以同时满足,主要是要判断不相等的情况。

解题思路:

对于相等的条件,直接进行集合合并即可;

对于不相等的条件,判断两者是否属于同一个集合,如果形成矛盾,则条件不能成立。

由于i,j的范围至10^9,定义并查集如果直接p[N],N=1e9+5,则会导致内存溢出

而n最多不过10^5,因此,可以对i,j进行离散化处理:

对出现的所有i,j进行排序、去重,用二分来找到i,j的下标代替其本身,这样,数据范围可以缩小到2 * 10^5规模。

所谓离散化,就是将一个范围比较大的数据集映射到一个范围较小的数据集的过程,这里代码处理如下:

首先,读入所有出现过的i,j,将其保存如vector<int> a

while(n--)
{
    cin >> i >> j >> e;
    a.push_back(i); 
    a.push_back(j);
}

再对a进行排序

sort(a.begin(), a.end());

再对a进行去重,有两种方法可以去重

1、STL去重法,借助于erase和unique函数,适合对STL有良好记忆的朋友

a.erase(unique(a.begin(), a.end()), a.end());

2、手动去重,将去重后的数据存入vector<int> b,适合不想记忆STL的朋友

for(int i = 0; i < a.size(); i++)
{
    if(i == 0 || a[i] != a[i - 1]) b.push_back(a[i]);
}

由于i,j的范围是1~10^9,但是一共只有n=10^5对,也就是最多有2*10^5个数,去重之后,b的size最多在2*10^5

因此,要将一个10^9范围的数映射到2*10^5范围,只需要用二分在b中查找数的下标即可,一个数对应唯一一个下标,对原数的处理都可以转为对下标的处理,这样在定义并查集的时候只需要定一个一个2*10^5长度的数组即可,就不会导致内存溢出问题,这就是离散化最大的作用。

下面给出完整代码。

100分代码:

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

const int N = 2e5 + 5;

struct node
{
    int i, j, e;
};

int p[N]; 
vector<int> a, b; //a保存所有i,j,用来做离散化处理, b是a排序、去重之后的结果
vector<node> c, d; //c保存所有相等条件,d保存所有不相等的条件

int t, n, i, j, e;

//在b中找到x所在的位置
int bs(int x)
{
    int l = 0, r = b.size() - 1, ans = -1;
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        if(b[mid] >= x) 
        {
            ans = mid;
            r = mid - 1;
        }
        else l = mid + 1;
    }
    return ans + 1; //使得下标从1开始
}

int find(int x)
{
    if(p[x] == x) return x;
    return p[x] = find(p[x]);
}

void merge(int x, int y)
{
    p[find(x)] = find(y);
}

int main()
{
    cin >> t;
    while(t--)
    {
        cin >> n;
        a.clear();
        b.clear();
        c.clear();
        d.clear();
        while(n--)
        {
            cin >> i >> j >> e;
            //将所有出现过的i,j存入a中
            a.push_back(i); 
            a.push_back(j);
            if(e == 1) c.push_back({i, j, e}); //将所有相等的条件存入c中
            if(e == 0) d.push_back({i, j, e}); //将所有不相等的条件存入d中
        }
        
        //对a排序
        sort(a.begin(), a.end());
        //对a去重,得到b
        //也可以直接a.erase(unique(a.begin(), a.end()), a.end());
        for(int i = 0; i < a.size(); i++)
        {
            if(i == 0 || a[i] != a[i - 1]) b.push_back(a[i]);
        }

        //初始化并查集
        for(int i = 1; i <= b.size(); i++) p[i] = i;
        //把所有相等的条件进行集合合并
        for(int i = 0; i < c.size(); i++)
        {
            int ni = bs(c[i].i), nj = bs(c[i].j);
            merge(ni, nj); 
        }
        bool yes = true;
        //判断不相等的条件是否有矛盾
        for(int i = 0; i < d.size(); i++)
        {
            int ni = bs(d[i].i), nj = bs(d[i].j);
            if(find(ni) == find(nj))
            {
                yes = false;
                break;
            }
        }
        if(yes) cout << "YES" << endl;
        else cout << "NO" << endl;
    }
    return 0;
}

 

posted @ 2024-03-26 15:07  五月江城  阅读(36)  评论(0编辑  收藏  举报