洛谷P1955 [NOI2015]程序自动分析

洛谷P1955 [NOI2015]程序自动分析

博客图片

题目链接

洛谷P1955 程序自动分析

题目概述

存在(n)个变量(x_1,x_2,dots,x_n),给出这(n)个变量之间的(n)个约束条件(x_i = x_j or x_i eq x_j)判断这些是否可以同时满足,如果可以输出YES,否则输出NO.一组测试包含多个测试,输入的开始给出测试的个数,每个测试相互独立.变量之间的约束条件输入的格式为(iquad jquad e),如果(e = 1)说明(x_i = x_j),如果(e=0)说明(x_i eq x_j.)

题目分析

可以先根据相等的条件把相等的变量划分到一个集合中,然后逐一的判断每个不相等的条件,取出这两个变量(x_i,x_j)如果(x_i,x_j)属于同一个值相等的集合,那么这个约束条件不满足,可以直接输出NO,当所有的不相等的约束条件都满足时输出YES.

并查集就是这样一种支持将元素按所属的集合进行分类,可以找到集合中任意一个元素所属的集合,核心操作是union_set(x,y)find_set(x).以每个集合的树高用于合并时的优化,和查询时采用路径压缩在找到元素所属集合的根节点后,把从根节点到这个元素结点路径上的所有结点的前驱改为根节点,从而在一次(O(log(n)))的时间福再度查询后,下一次可以以(O(1))的时间复杂度获得这个元素的集合的根节点.经过这样的union_setfind_set操作,最后得到的并查集的一个类似于这种形式:

graph TD r((r)) a((a)) b((b)) dots((...)) k((k)) r---a r---b r---dots r---k

注意:但是题目中对于(i,j)范围的限定是(1leq i,j leq 10^9),如果直接开一个(10^{10})的数组,外加一些其它的空间开销,是没法分配这样大的空间的,如果数组开的小,会有一个测试点出现RE,一个可行的方案是对输入的数据进行离散化处理,输入的数据最多有(2n)个,而(n)的范围是(1leq 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;
}

其它

原文地址:https://www.cnblogs.com/2018slgys/p/13414051.html