P1955 [NOI2015]程序自动分析 && 离散化学习 && lower_bound学习

P1955 [NOI2015]程序自动分析

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

题目分析
这道题是一个很显然的并查集,但是看到数据范围
我们竟然需要开一个1e9的数组!
那么很显然的会MLE
所以我们要用到离散化
然后就可以了

参考代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
//#define DEBUG(x) cerr << #x << "=" << x << endl
const int maxn = 1e5 + 7;

int n, t, f[maxn], book[maxn * 3];

struct node
{
    int x;
    int y;
    int e;
}a[maxn];

bool cmp(node a, node b)
{
    return a.e > b.e;
}

inline void first(int k)
{
    for (int i = 1; i <= k; i++) f[i] = i;
}

int get(int x)
{
    return f[x] == x ? x : f[x] = get(f[x]);
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> t;
    while (t--)
    {
        cin >> n;
        int tot = -1;
        int flag = 1;
        memset(a, 0, sizeof(a));
        memset(f, 0, sizeof(f));
        memset(book, 0, sizeof(book));
        for (int i = 1; i <= n; i++)
        {
            cin >> a[i].x >> a[i].y >> a[i].e;
            book[++tot] = a[i].x;
            book[++tot] = a[i].y;
        }
        sort(book, book + tot);
        int reu = unique(book, book + tot) - book;
        for (int i = 1; i <= n; i++)
        {
            a[i].x = lower_bound(book, book + reu, a[i].x) - book;
            a[i].y = lower_bound(book, book + reu, a[i].y) - book;
        }
        first(reu);
        sort(a + 1, a + n + 1, cmp);
        for (int i = 1; i <= n; i++)
        {
            int r1 = get(a[i].x);
            int r2 = get(a[i].y);
            if (a[i].e)
            {
                f[r1] = r2;
            }
            else if (r1 == r2)
            {
                cout << "NO" << endl;
                flag = 0;
                break;
            }
        }
        if (flag)
            cout << "YES" << endl;
    }
    return 0;
}

离散化

定义
离散化,把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。
通俗的说,离散化是在不改变数据相对大小的条件下,对数据进行相应的缩小。例如:
原数据:1, 999, 100000, 15;
处理后:1, 3, 4, 2;
原数据:{100, 200},{20, 50000},{1, 400};
处理后:{3, 4},{2, 6},{1, 5};
对于离散化来说主要有三个步骤
一.去重(用unique去重函数)
二.排序
三.二分索引(用lower_bound函数)

const int N = 1e5 + 7;

int t[N], a[N];

int main()
{
    std::ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
           cin >> a[i];
        t[i] = a[i];
    }
    sort(t + 1, t + n + 1);
    m = unique(t + 1, t + n + 1) - t - 1;
    for (int i = 1; i <= n; i++)
        a[i] = lower_bound(t + 1, t + m + 1, a[i]) - t;
}

在这段代码中,a[ ]经过离散,范围就变成了m。解释一下,unique是c++自带的一个函数,表示对一个数列去重,然后返回不重复的元素个数,当然在后面要减去首地址。那么这种离散化对于有重复元素的数列也可以适用,但是复杂度会高些。有一种离散化的方式复杂度会低一些,但是不能处理有重复元素的数列,所以在此不再赘述。
举个例子:原数据:{6, 8, 4, 9, 5, 6, 7, 4},首先排序后得到{4, 4, 5, 6, 6, 7, 8, 9},去重{4, 5, 6, 7, 8, 9},然后原序列就变成了{3, 5, 1, 6, 2, 3, 4, 1}。

lower_bound( )

这里是关于lower_bound( )和upper_bound( )的常见用法。
lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的,这两个函数都需要加载头文件#include<algorithm>
从小到大
的排序数组中,
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
从大到小的排序数组中,重载lower_bound()和upper_bound()
lower_bound( begin,end,num,greater() ):从数组的begin位置到end-1位置二分查找第一个小于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num,greater() ):从数组的begin位置到end-1位置二分查找第一个小于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
//#define DEBUG(x) cerr << #x << "=" << x << endl
#define LL long long
const int maxn = 1e5 + 10;
const int INF = 2 * int(1e9) + 10;

int cmd(int a, int b)
{
    return a > b;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int num[6] = {1, 2, 4, 7, 15, 34};
    sort(num, num + 6);                                             //按从小到大排序
    int pos1 = lower_bound(num, num + 6, 7) - num;                    //返回数组中第一个大于或等于被查数的值
    int pos2 = upper_bound(num, num + 6, 7) - num;                    //返回数组中第一个大于被查数的值
    cout << pos1 << " " << num[pos1] << endl;
    cout << pos2 << " " << num[pos2] << endl;
    sort(num, num + 6, cmd);                                           //按从大到小排序
    int pos3 = lower_bound(num, num + 6, 7, greater<int>()) - num;  //返回数组中第一个小于或等于被查数的值
    int pos4 = upper_bound(num, num + 6, 7, greater<int>()) - num;  //返回数组中第一个小于被查数的值
    cout << pos3 << " " << num[pos3] << endl;
    cout << pos4 << " " << num[pos4] << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值