Codechef Cloning(主席树 + 哈希)

题意

n n n 个数 { a i } \{a_i\} {ai},有 m m m 个询问,每个询问给定 l 1 , r 1 , l 2 , r 2 l_1,r_1,l_2,r_2 l1,r1,l2,r2,设 { b i } \{b_i\} {bi} [ l 1 , r 1 ] [l_1,r_1] [l1,r1] 排完序后的数组, { c i } \{c_i\} {ci} [ l 2 , r 2 ] [l_2,r_2] [l2,r2] 排完序后的数组,问两个数组中是否至多有一位不同。
其中, n , m , a i ≤ 1 0 5 n,m,a_i\leq 10^5 n,m,ai105

分析

先将 a i a_i ai 进行哈希,得到 v a i v_{a_i} vai,假如 [ l 1 , r 1 ] [l_1,r_1] [l1,r1] [ l 2 , r 2 ] [l_2,r_2] [l2,r2] 完全相同,那么 ∑ i = l 1 r 1 v a i = ∑ i = l 2 r 2 v a i \sum\limits_{i=l_1}^{r_1}v_{a_i}=\sum\limits_{i=l_2}^{r_2}v_{a_i} i=l1r1vai=i=l2r2vai
然后我们用主席树维护哈希值,每个节点的值为哈希值的和。假如有一个不同,我们可以在主席树上二分得到第一个不同的数 p p p 和最后一个不同的数 q q q
那么排完序后这两个数组相同的条件就是 p , q p,q p,q 的个数分别相差 1 1 1 [ p + 1 , q − 1 ] [p+1,q-1] [p+1,q1] 中没有数存在于 [ l 1 , r 1 ] [l_1,r_1] [l1,r1] [ l 2 , r 2 ] [l_2,r_2] [l2,r2] 中。为啥呢? 因为 我们已经比较了 [ 1 , p ] [1,p] [1,p] [ q + 1 , m a x n ] [q+1,maxn] [q+1,maxn]。要保证 p p p q q q 在可以两个数组的同一位,只需要保证两个数之间没有多的数即可,否则他们不可能在两个数组的同一位。(这里可能需要好好捋一捋)
复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

代码如下

#include <bits/stdc++.h>
#include<ext/pb_ds/hash_policy.hpp>
#include<ext/pb_ds/assoc_container.hpp>
#define lson l, m, lch[rt]
#define rson m + 1, r, rch[rt]
#define int unsigned long long
#define N 100005
using namespace __gnu_pbds;
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
LL z = 1;
int read(){
	int x, f = 1;
	char ch;
	while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') f = -1;
	x = ch - '0';
	while(ch = getchar(), ch >= '0' && ch <= '9') x = x * 10 + ch - 48;
	return x * f;
}
int ksm(int a, int b, int p){
	int s = 1;
	while(b){
		if(b & 1) s = z * s * a % p;
		a = z * a * a % p;
		b >>= 1;
	}
	return s;
}
mt19937_64 rdn(time(0));
int lch[N * 30], rch[N * 30], s[N], sum[N * 30], root[N], v[N], a[N], cnt, maxn = N - 5;
void clr(){
	cnt = 0;
	memset(lch, 0, sizeof(lch));
	memset(rch, 0, sizeof(rch));
	memset(sum, 0, sizeof(sum));
	memset(root, 0, sizeof(root));
	memset(v, 0, sizeof(v));
}
void build(int l, int r, int &rt){
	rt = ++cnt;
	if(l == r) return;
	int m = l + r >> 1;
	build(lson);
	build(rson);
}
void update(int l, int r, int &rt, int las, int p){
	rt = ++cnt;
	sum[rt] = sum[las] + v[p];
	lch[rt] = lch[las], rch[rt] = rch[las];
	if(l == r) return;
	int m = l + r >> 1;
	if(p <= m) update(lson, lch[las], p);
	else update(rson, rch[las], p);
}
int q1(int l, int r, int r1, int r2, int r3, int r4){
	if(l == r) return l;
	int m = l + r >> 1;
	if(sum[lch[r1]] - sum[lch[r2]] != sum[lch[r3]] - sum[lch[r4]]) return q1(l, m, lch[r1], lch[r2], lch[r3], lch[r4]);
	return q1(m + 1, r, rch[r1], rch[r2], rch[r3], rch[r4]);
}
int q2(int l, int r, int r1, int r2, int r3, int r4){
	if(l == r) return l;
	int m = l + r >> 1;
	if(sum[rch[r1]] - sum[rch[r2]] != sum[rch[r3]] - sum[rch[r4]]) return q2(m + 1, r, rch[r1], rch[r2], rch[r3], rch[r4]);
	return q2(l, m, lch[r1], lch[r2], lch[r3], lch[r4]);
}

int qsum(int l, int r, int rt, int las, int a, int b){
	if(l >= a && r <= b) return sum[rt] - sum[las];
	int m = l + r >> 1, ans = 0;
	if(a <= m) ans += qsum(lson, lch[las], a, b);
	if(b > m) ans += qsum(rson, rch[las], a, b);
	return ans;
}

int gs(int l, int r, int x, int y){
	if(x > y) return 0;
	return qsum(1, maxn, root[r], root[l - 1], x, y);
}


main(){
	int i, j, n, m, t, T, l, r, x, y, p, q, w1, w2, w3, w4;
	T = read();
	while(T--){
		clr();
		n = read(); m = read();
		for(i = 1; i <= n; i++) a[i] = read();
		for(i = 1; i <= maxn; i++) v[i] = rdn();
		for(i = 1; i <= n; i++) s[i] = s[i - 1] + v[a[i]];
		build(1, maxn, root[0]);
		for(i = 1; i <= n; i++) update(1, maxn, root[i], root[i - 1], a[i]);
		for(i = 1; i <= m; i++){
			l = read(); r = read(); x = read(); y = read(); 
			if(s[r] - s[l - 1] == s[y] - s[x - 1]) printf("YES\n");
			else{	
				p = q1(1, maxn, root[r], root[l - 1], root[y], root[x - 1]);
				q = q2(1, maxn, root[r], root[l - 1], root[y], root[x - 1]);
				w1 = gs(l, r, p, p) - gs(x, y, p, p); w2 = gs(l, r, q, q) - gs(x, y, q, q);
				w3 = gs(l, r, p + 1, q - 1), w4 = gs(x, y, p + 1, q - 1);
				if(((w1 == v[p] && w2 + v[q] == 0) || (w2 == v[q] && w1 + v[p] == 0)) && w3 == 0 && w4 == 0) printf("YES\n");
				else printf("NO\n");
			}
		}
	}
	return 0;
}
/*
1
6 5
1 3 4 2 3 4
1 3 4 6
2 5 3 6
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值