bzoj4939: [Ynoi2016]掉进兔子洞
Description
一个长为 n 的序列 a。
有 m 个询问,每次询问三个区间,把三个区间中同时出现的数一个一个删掉,问最后三个区间剩下的数的个数和,询问独立。
注意这里删掉指的是一个一个删,不是把等于这个值的数直接删完,
比如三个区间是 [1,2,2,3,3,3,3] , [1,2,2,3,3,3,3] 与 [1,1,2,3,3],就一起扔掉了 1 个 1,1 个 2,2 个 3。
Input
第一行两个数表示 n , m。
第二行 n个数表示 a[i]。
之后 m 行,每行 6 个数 l1 , r1 , l2, r2 , l3 , r3 表示这三个区间。
Output
对于每个询问,输出一个数表示答案。
Sample Input
5 2
1 2 2 3 3
1 2 2 3 3 4
1 5 1 5 1 5
Sample Output
3
0
HINT
n , m <= 100000 , 1 <= a[i] <= 1000000000
分析
一个很神仙的做法。
A
n
s
=
∑
l
e
n
−
3
∑
m
a
x
(
c
n
t
1
,
c
n
t
2
,
c
n
t
3
)
Ans=\sum len-3\sum max(cnt_1,cnt_2,cnt_3)
Ans=∑len−3∑max(cnt1,cnt2,cnt3)
就是用长度减去公共部分。
考虑
b
i
t
s
e
t
bitset
bitset
但是
b
i
t
s
e
t
bitset
bitset只能维护有多少种相同的数,不能维护有多少个公共的数。
有一种神奇的方法。离散化的时候定义每个数的权值为小于等于这个数的数的个数。
把某个数
p
p
p放入
b
i
t
s
e
t
bitset
bitset里面的时候,假设之前这个数放了
c
n
t
[
p
]
cnt[p]
cnt[p]个,把这个数放在
b
i
t
s
e
t
bitset
bitset的
p
−
c
n
t
[
p
]
p-cnt[p]
p−cnt[p]位置上。
这样的做法显然是正确的。因为放了多少个数,在那个数的区间内就会有多少个后缀1
然后就用莫队+bitset。
空间显然是开不下的,所以拆成三次,重复做三遍莫队即可。
时间复杂度
O
(
n
n
+
n
2
64
)
O(n\sqrt n+\frac{n^2}{64})
O(nn+64n2),空间复杂度
O
(
n
2
64
)
O(\frac{n^2}{64})
O(64n2)
代码
手写 b i t s e t bitset bitset真好玩~
#include<bits/stdc++.h>
const int M = 34010, N = 1e5 + 10;
typedef unsigned long long ULL;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
ULL All = 0;
int fs[65536], cnt[N], A[N], a[N], b[N], tot = 1, n, m, tp, B;
struct Bit {
ULL s[1564];
void Ini() {for(int i = 0;i <= 1563; ++i) s[i] = All;}
void operator &= (Bit a) {for(int i = 0;i <= 1563; ++i) s[i] &= a.s[i];}
void Add(int p) {s[p >> 6] ^= (ULL)1 << (p & 63);}
int Cnt() {
int r = 0;
for(int i = 0;i <= 1563; ++i)
for(ULL x = s[i]; x; x >>= 16)
r += fs[x & 65535];
return r;
}
}c[M], nw;
struct Que {
int l, r, id;
void in(int i) {l = ri(); r = ri(); id = i;}
}q[M * 3];
void Add(int v, int p) {
if(!~p) cnt[v] += p;
nw.Add(v - cnt[v]);
if(~p) cnt[v] += p;
}
bool cmp(Que a, Que b) {
int x = a.l / B, y = b.l / B;
return x == y ? ((x & 1) ? a.r < b.r : a.r > b.r) : x < y;
}
void Solve() {
if(tot > m) return ;
for(int i = 1;i <= M - 10 && tot <= m; ++i, ++tot)
for(int k = 3; k--; )
q[++tp].in(i), A[i] += q[tp].r - q[tp].l + 1;
for(int i = 1;i <= tp / 3; ++i) c[i].Ini();
B = sqrt(tp); std::sort(q + 1, q + tp + 1, cmp);
int L = 1, R = 0;
for(int i = 1;i <= tp; ++i) {
for(;R < q[i].r;) Add(a[++R], 1);
for(;L > q[i].l;) Add(a[--L], 1);
for(;L < q[i].l;) Add(a[L++], -1);
for(;R > q[i].r;) Add(a[R--], -1);
c[q[i].id] &= nw;
}
for(int i = L;i <= R; ++i) Add(a[i], -1);
for(int i = 1;i <= tp / 3; ++i) printf("%d\n", A[i] - c[i].Cnt() * 3), A[i] = 0; tp = 0;
}
int F(int x) {
if(x > b[n]) return n + 1;
int L = 1, R = n, m;
for(;L != R; b[m = L + R >> 1] >= x ? R = m : L = m + 1) ;
return L;
}
int main() {
for(int i = 0;i < 64; ++i) All |= (ULL)1 << i;
for(int i = 1;i < 65536; ++i) fs[i] = fs[i ^ (i&-i)] + 1;
n = ri(); m = ri();
for(int i = 1;i <= n; ++i) b[i] = a[i] = ri();
std::sort(b + 1, b + n + 1);
for(int i = 1;i <= n; ++i) a[i] = F(a[i] + 1) - 1;
Solve(); Solve(); Solve();
return 0;
}