【ybtoj高效进阶 21274】相似序列(主席树)(哈希)(二分)
相似序列
题目链接:ybtoj高效进阶 21274
题目大意
给你一个数组,多次询问,每次问你两个长度相等的子区间,问你把这两个区间排序之后,是否至多只有一个位置的数不同。
思路
看到这些不难想到用个什么数据结构维护出一个区间的每个数的出现次数。
然后把每个数的出现次数用个哈希搞出来来快速判断是否相同。
然后看到区间,每个值,不难想到主席树。
然后接着考虑如何判断,首先如果直接哈希值相同就可以。
那如果不相同,我们可以通过一个小小的二分找到它第一个有不同出现次数的数。
然后我们考虑一下如果只有一个位置不同那会怎么样。
首先就只会有两个数值的出现次数不同,接着就是这两个数值中间是不能掺杂其它数的。
那我们不妨把接下来的一个有出现的数找到,那后面的数肯定就是要全部匹配上了。
然后你还发现,当然是两个数值的出现次数不同的大小只能是 \(1\),而且要一个多 \(1\),一个少 \(1\)。
后面的条件换句话说,就是要这两个数的出现次数和是一样的。
然后就按着上面的判断一下就好了。
代码
#include<cstdio>
#define mo 131
#define ll unsigned long long
using namespace std;
int n, q, l1, l2, r1, r2, a[100005];
ll di[100005];
int Abs(int x) {
return x < 0 ? -x : x;
}
struct XD_tree {//小小的主席树awa
int rt[100005], tot;
int ls[100005 << 6], rs[100005 << 6];
ll val[100005 << 6];
void up(int now, int l, int r) {
int mid = (l + r) >> 1;
val[now] = val[ls[now]] * di[r - mid] + val[rs[now]];
}
int copy(int x) {
int now = ++tot;
ls[now] = ls[x]; rs[now] = rs[x];
val[now] = val[x];
return now;
}
int insert(int now, int l, int r, int pl) {
now = copy(now);
if (l == r) {
val[now]++;
return now;
}
int mid = (l + r) >> 1;
if (pl <= mid) ls[now] = insert(ls[now], l, mid, pl);
else rs[now] = insert(rs[now], mid + 1, r, pl);
up(now, l, r);
return now;
}
ll query(int now, int l, int r, int L, int R) {
if (!now || L > R) return 0;
if (L <= l && r <= R) {
return val[now];
}
int mid = (l + r) >> 1;
if (L > mid) return query(rs[now], mid + 1, r, L, R);
if (mid >= R) return query(ls[now], l, mid, L, R);
ll x = query(ls[now], l, mid, L, R), y = query(rs[now], mid + 1, r, L, R);
return x * di[R - mid] + y;
}
ll get_va_(int l, int r, int vl, int vr) {
if (vl > vr) return 0;
return query(rt[r], 1, 100000, vl, vr) - query(rt[l - 1], 1, 100000, vl, vr);
}
}T;
int get_L(int l1, int r1, int l2, int r2) {
int l = 1, r = 100000, ans = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (T.get_va_(l1, r1, 1, mid) != T.get_va_(l2, r2, 1, mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
return ans;
}
int get_nxt(int l1, int r1, int l2, int r2, int X) {
int l = X + 1, r = 100000, ans = X;
while (l <= r) {
int mid = (l + r) >> 1;
if (T.get_va_(l1, r1, X + 1, mid) || T.get_va_(l2, r2, X + 1, mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
return ans;
}
int main() {
// freopen("similar.in", "r", stdin);
// freopen("similar.out", "w", stdout);
scanf("%d %d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
di[0] = mo;
for (int i = 1; i <= 100000; i++)
di[i] = di[i - 1] * mo;
for (int i = 1; i <= n; i++) {
T.rt[i] = T.insert(T.rt[i - 1], 1, 100000, a[i]);
}
while (q--) {
scanf("%d %d %d %d", &l1, &r1, &l2, &r2);
if (T.get_va_(l1, r1, 1, 100000) == T.get_va_(l2, r2, 1, 100000)) {
printf("YES\n");
}
else {
ll X = get_L(l1, r1, l2, r2);//找到第一个不同的
ll Y = get_nxt(l1, r1, l2, r2, X);//找到接下来的一个数
int x1 = T.get_va_(l1, r1, X, X), y1 = T.get_va_(l2, r2, X, X);
int x2 = T.get_va_(l1, r1, Y, Y), y2 = T.get_va_(l2, r2, Y, Y);
if (x1 + x2 == y1 + y2 && Abs(x1 - y1) <= 1 && T.get_va_(l1, r1, Y + 1, 100000) == T.get_va_(l2, r2, Y + 1, 100000)) printf("YES\n");
else printf("NO\n");
}
}
return 0;
}