洛谷P1955 [NOI2015]程式自動分析
洛谷P1955 [NOI2015]程式自動分析
題目連結
題目概述
存在\(n\)個變數\(x_1,x_2,\dots,x_n\),給出這\(n\)個變數之間的\(n\)個約束條件\(x_i = x_j \ or \ x_i \neq x_j\)判斷這些是否可以同時滿足,如果可以輸出YES
,否則輸出NO
.一組測試包含多個測試,輸入的開始給出測試的個數,每個測試相互獨立.變數之間的約束條件輸入的格式為\(i\quad j\quad e\),如果\(e = 1\)說明\(x_i = x_j\),如果\(e=0\)說明\(x_i \neq x_j.\)
題目分析
可以先根據相等的條件把相等的變數劃分到一個集合中,然後逐一的判斷每個不相等的條件,取出這兩個變數\(x_i,x_j\)
NO
,當所有的不相等的約束條件都滿足時輸出YES
.
並查集就是這樣一種支援將元素按所屬的集合進行分類,可以找到集合中任意一個元素所屬的集合,核心操作是union_set(x,y)
和find_set(x)
.以每個集合的樹高用於合併時的優化,和查詢時採用路徑壓縮在找到元素所屬集合的根節點後,把從根節點到這個元素結點路徑上的所有結點的前驅改為根節點,從而在一次\(O(log(n))\)的時間福再度查詢後,下一次可以以\(O(1)\)的時間複雜度獲得這個元素的集合的根節點.經過這樣的union_set
和find_set
注意:但是題目中對於\(i,j\)範圍的限定是\(1\leq i,j \leq 10^9\),如果直接開一個\(10^{10}\)的陣列,外加一些其它的空間開銷,是沒法分配這樣大的空間的,如果陣列開的小,會有一個測試點出現RE
,一個可行的方案是對輸入的資料進行離散化處理,輸入的資料最多有\(2n\)個,而\(n\)的範圍是\(1\leq n \leq 10^5\)
10000 1999999 1
199999 99999999 0
92713821 23713324 1
10000 23713324 0
很明顯這樣的資料如果直接用陣列下標對映,陣列的開銷非常大,但實際上輸入的資料非常少,把輸入的每一個變數儲存起來,得到這樣的一個序列:
10000 1999999 199999 99999999 92713821 23713324 10000 23713324
然後按升序進行排列去重,得到:
10000 199999 1999999 23713324 92713821 99999999
這樣就建立了原來資料和現在資料在序列中下標的對映:
\[buf[0]=10000,buf[1]=199999,\dots \]
如果需要查詢某個數\(x\)在上面的有序序列中,可以用STL
標準庫裡面的lower_bound
方法,返回值是區間\([first,last)\)第一個不小於\(x\)的數在這個區間的位置,如果沒有,那麼返回\(last\),因為上面的序列是單調遞增的,並且查詢的\(x\)一定位於其中,所以最後得到的一定是數\(x\)在\(buf\)中的位置.
經過上面的序列化處理後,利用並查集的模板可以輕鬆方便的解決這個題目
程式碼實現
/*
* @Author: Shuo Yang
* @Date: 2020-08-01 08:08:32
* @LastEditors: Shuo Yang
* @LastEditTime: 2020-08-01 10:50:03
* @FilePath: /Code/luogu/P1955.cpp
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+5;
int s[N];
int heights[N];
int buf[N];
//並查集模板
void init_set(){
memset(heights, 0,sizeof(heights));
for(int i = 0;i < N; ++i)
s[i] = i;
}
int find_set(int x){
int r = x;
while( s[r] != r){
r = s[r];
}
int i = x;
while( i != r){
int j = s[i];
s[i] = r;
i = j;
}
return r;
}
void union_set(int x, int y){
x = find_set(x);
y = find_set(y);
if( heights[x] == heights[y]){
heights[x]++;
s[y] = x;
}else{
if( heights[x] < heights[y])
s[x] = y;
else
s[y] = x;
}
}
//這道題實際處理的部分
struct node{
int a,b;
};
node equ[N];
node nequ[N];
int cnt = 0;
int get_index(int x){
return lower_bound(buf, buf+cnt, x) - buf;
}
void solve(int n, int m){
init_set();
for(int i = 0; i < n; ++i){
union_set(get_index(equ[i].a), get_index(equ[i].b));
}
for(int i = 0; i < m; ++i){
if(find_set(get_index(nequ[i].a)) == find_set(get_index(nequ[i].b))){
cout<<"NO"<<endl;
return;
}
}
cout<<"YES"<<endl;
}
int main(int argc, const char** argv) {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
cin>>t;
while(t--){
int k;
cin>>k;
int n = 0;
int m = 0;
cnt = 0;
for(int i = 0; i < k; ++i){
int a,b,f;
cin>>a>>b>>f;
if(f==1){
equ[n++] = {a,b};
}else{
nequ[m++] = {a,b};
}
buf[cnt++] = a;
buf[cnt++] = b;
}
//排序去重
sort(buf, buf+cnt);
cnt = unique(buf, buf+cnt)-buf;
solve(n,m);
}
return 0;
}