离散化简介

离散化

如果有一些数组,元素个数不多,但是元素值的跨度很大。我们可以将它们的值替换成小一点的数,使它们的值变得紧凑,同时大小关系仍然与原数组一样。

比如有一个数组 a = { 1 , 1000000 , 100 , 30000 , 999999999 } a=\{1,1000000,100,30000,999999999\} a={1,1000000,100,30000,999999999},我们可以把它替换为 a ′ = { 1 , 4 , 2 , 3 , 5 } a'=\{1,4,2,3,5\} a={1,4,2,3,5}

并记住对应关系。这样替换以后,很多问题就很好办了。

离散化这样操作:

设原数组为 a a a, 将原数组复制一份,设为数组 b b b,对 b b b数组排序,去重,然后对原数组 a a a的每个元素 a i a_i ai,在 b b b数组中查找 a i a_i ai, 用查找到的位置来代替 a i a_i ai.

sort(b + 1, b + n + 1);
int tot = unique(b + 1, b + n + 1) - (b + 1);
for(int i = 1; i <= n; i++){
    a[i] = lower_bound(b + 1, b + tot + 1, a[i]) - b;
}

例1. 用树状数组求逆序对的个数

给一个数组 a a a,有n个元素,求数组中的逆序对的个数。

n ≤ 100000 , 1 ≤ a i ≤ 1 0 9 n\leq 100000,1 \leq a_i \leq 10^9 n1000001ai109

分析

设树状数组为 t r e e tree tree

将数组 a a a从左到右访问,访问到 a [ i ] a[i] a[i]时,

首先在 t r e e tree tree先执行 g e t s u m ( a [ i ] ) getsum(a[i]) getsum(a[i]),求出前缀和,这个前缀和就是在 a [ i ] a[i] a[i]左边小于 a [ i ] a[i] a[i]的元素个数。

然后再执行 a d d ( a [ i ] , 1 ) add(a[i], 1) add(a[i],1)

t r e e tree tree数组要用 a [ i ] a[i] a[i]的值做下标,但 a [ i ] a[i] a[i]的值太多,必须离散化。

#include <bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define lowbit(a) (a & -(a))
#define LL long long int
int n, tot, num[MAXN], tmpnum[MAXN], rk[MAXN], tree[MAXN];
LL ans;
int getsum(int x){
	int res = 0;
	while(x){
		res += tree[x];
		x -= lowbit(x);
	} 
	return res;
}
void update(int x){
	while(x <= tot){
		tree[x]++;
		x += lowbit(x);
	}	
}
int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; i++){
		scanf("%d", &num[i]);
		tmpnum[i] = num[i];
	}
	sort(num + 1, num + n + 1);
	tot = unique(num + 1, num + n + 1) - num - 1; //unique为去重函数,返回去重后的数组的尾地址
    //下面的rk数组即为离散化过后的数组
	for(int i = 1; i <= n; i++)
        rk[i] = lower_bound(num + 1, num + tot + 1, tmpnum[i]) - num;
    
	for(int i = 1; i <= n; i++){
		ans += (i - 1 - getsum(rk[i]));
		update(rk[i]);
	}
	printf("%lld\n", ans);
	return 0;
} 
例2. parity

有一个01序列,长度≤1000000000,现在有n条信息,每条信息的形式是"a b even/odd", 表示序列的第a位到第b位元素之间的元素总和是偶数/奇数。 你的任务是对于这些给定的信息,输出第一个不正确的信息所在位置-1。信息的数目不超过5000。 如果信息全部正确,即可以找到一个满足要求的01序列,那么输出n。

分析

因为序列长度太大,而实际查询的次数最多只有5000,即位置数最多5000*2个,这就是fa数组元素个数了。

长度太大,所以需要离散化。

然后再用带权并查集处理即可。

如查询: a    b    e v e n / o d d a \; b \; even/odd abeven/odd, 即在 b → ( a − 1 ) b\to (a-1) b(a1)连一条权值为0/1的边.

如果当前查询的两个点之前已经联通了,则判断是否矛盾,如果矛盾,则该次查询就是假话。

#include <bits/stdc++.h>
using namespace std;
#define MAXN 5005
struct query{
    int a, b;
    bool flg;
}que[MAXN];
int n, m, rk[MAXN << 1], tmp[MAXN << 1];
int fa[MAXN << 1], w[MAXN << 1];
int a, b, ans;
char s[5];
int getroot(int x){
    if(fa[x] == 0)return x;
    int tmp = getroot(fa[x]);
    w[x] ^= w[fa[x]];
    return fa[x] = tmp;
}
void my_union(int a, int b, bool flg){
    int ra = getroot(a), rb = getroot(b);
    if(ra != rb){
        fa[ra] = rb;
        w[ra] = (w[a] ^ w[b] ^ flg);
    }
}
int main(){
    scanf("%d %d", &m, &n);
    for(int i = 1; i <= n; i++){
        scanf("%d %d %s", &que[i].a, &que[i].b, s);
        if(s[0] == 'o') que[i].flg = 1;
        else que[i].flg = 0;
        tmp[i * 2 - 1] = --que[i].a;
        tmp[i * 2] = que[i].b ;
    }
    int tot = n << 1;
    sort(tmp + 1, tmp + tot + 1);
    tot = unique(tmp + 1, tmp + tot + 1) - (tmp + 1);
    for(int i = 1; i <= n; i++){
        que[i].a = lower_bound(tmp + 1, tmp + tot + 1, que[i].a) - tmp;
        que[i].b = lower_bound(tmp + 1, tmp + tot + 1, que[i].b) - tmp;
    }
    for(int i = 1; i <= n; i++){
        int ta = getroot(que[i].a), tb = getroot(que[i].b);
        if(ta == tb){
            if((w[que[i].a] ^ w[que[i].b]) !=  que[i].flg){
                printf("%d\n", i - 1);
                return 0;
            }
        }
        else
            my_union(que[i].a, que[i].b, que[i].flg);
    }
    printf("%d\n", n);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值