洛谷題單指南-集合-P1955 [NOI2015] 程式自動分析

江城伍月發表於2024-03-26

原題連結: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;
}

相關文章