题目链接
https://ac.nowcoder.com/acm/contest/121107/H
焰与霜的共鸣
题目描述
在
S
u
r
t
r
Surtr
Surtr 的记忆中,存在着大量彼此矛盾的片段——某些发生在雪原,某些在城市,有的甚至不属于已知的任何地理坐标。
S
u
r
t
r
Surtr
Surtr的记忆可以被量化为一列整数序列
S
=
{
a
1
,
a
2
,
…
,
a
n
}
S = \{a_1, a_2, \dots, a_n\}
S={a1,a2,…,an}。
每个整数代表一个“记忆节点”的能量波动。不同的区间
[
ℓ
,
r
]
[\ell, r]
[ℓ,r],对应着不同阶段的情绪与记忆片段。当
S
u
r
t
r
Surtr
Surtr 集中注意力时,会将这些片段重组,形成无数种可能的“记忆组合”。
每一个记忆组合都能产生特定的“共鸣值”,某种隐藏的极值模式存在于这些记忆片段之间,我们需要找到这种极值模式。
为了研究这种极值模式,我们将数据形式化为以下模型:
设有序整数序列(或多重集合) S = { a 1 , a 2 , … , a n } . S = \{ a_1, a_2, \dots, a_n \}. S={a1,a2,…,an}.
对任意下标区间 ( 1 ≤ ℓ ≤ r ≤ n ) (1 \le \ell \le r \le n) (1≤ℓ≤r≤n),定义区间多重集合 S ℓ , r = { a ℓ , a ℓ + 1 , … , a r } . S_{\ell, r} = \{ a_\ell, a_{\ell+1}, \dots, a_r \}. Sℓ,r={aℓ,aℓ+1,…,ar}.
由 S ℓ , r S_{\ell, r} Sℓ,r 的所有非空子集(这里子集按多重集合定义,即对每个元素可取或不取)产生的 子集和集合 记为
T ℓ , r = { ∑ x ∈ U x | U ⊆ S ℓ , r , U ≠ ∅ } . T_{\ell, r} = \left\{ \sum_{x \in U} x \;\middle|\; U \subseteq S_{\ell, r},U\neq \varnothing\right\}. Tℓ,r={x∈U∑x U⊆Sℓ,r,U=∅}.
(注意:若 S ℓ , r S_{\ell, r} Sℓ,r 含重复元素,则不同的选择可能产生相同的和, T ℓ , r T_{\ell, r} Tℓ,r 作为集合只保留不同的和。)
令 R R R 为 T ℓ , r T_{\ell, r} Tℓ,r 的任一 非空子集,即 R ⊆ T ℓ , r , R ≠ ∅ . R \subseteq T_{\ell, r}, \quad R \neq \varnothing. R⊆Tℓ,r,R=∅.
对任意集合 X X X 定义:
- mex \operatorname{mex} mex 运算: mex ( X ) = min { k ∈ N ∣ k ∉ X } . \operatorname{mex}(X) = \min \{ k \in \mathbb{N} \mid k \notin X \}. mex(X)=min{k∈N∣k∈/X}.
- gcd \gcd gcd 运算: gcd ( X ) = gcd ( x 1 , x 2 , … , x k ) , 若 X = { x 1 , … , x k } . \gcd(X) = \gcd(x_1, x_2, \dots, x_k), \quad \text{若 } X = \{x_1, \dots, x_k\}. gcd(X)=gcd(x1,x2,…,xk),若 X={x1,…,xk}.
给定
q
q
q 次查询,每次查询给出一个区间
[
ℓ
,
r
]
[\ell, r]
[ℓ,r]。
每次查询的答案为
max
R
⊆
T
ℓ
,
r
R
≠
∅
(
mex
(
R
)
⊕
gcd
(
R
)
)
,
\max_{ \substack{R \subseteq T_{\ell, r} \\ R \neq \varnothing}} \big( \operatorname{mex}(R) \oplus \gcd(R) \big),
R⊆Tℓ,rR=∅max(mex(R)⊕gcd(R)),
其中 “ ⊕ \oplus ⊕” 表示按位异或运算。
【名词解释】
- m e x \mathrm{mex} mex:整数数组的 m e x \mathrm{mex} mex 定义为没有出现在数组中的最小非负整数。例如: m e x ( { 1 , 2 , 3 } ) = 0 、 m e x ( { 0 , 2 , 2 , 5 } ) = 1 \mathrm{mex}(\{1,2,3\}) = 0、\mathrm{mex}(\{0,2,2,5\}) = 1 mex({1,2,3})=0、mex({0,2,2,5})=1
- g c d \mathrm{gcd} gcd :即最大公因数,指多个整数共有因数中最大的一个。例如,例如, 12 12 12、 18 18 18 和 30 30 30 的公因数有 1 , 2 , 3 , 6 1, 2, 3, 6 1,2,3,6, 其中最大的因数是 6 6 6,因此 g c d ( 12 , 18 , 30 ) = 6. \mathrm{gcd}(12, 18, 30) = 6. gcd(12,18,30)=6.特别地,定义 g c d ( x ) = x , g c d ( 0 , 0 ) = 0 , g c d ( 0 , x ) = x . \mathrm{gcd}(x) = x,\mathrm{gcd}(0,0)=0,\mathrm{gcd}(0,x)=x. gcd(x)=x,gcd(0,0)=0,gcd(0,x)=x.
- ⊕ \oplus ⊕ 代表按位异或。对两个整数的二进制表示按位进行异或运算,只有两个对应位不同时才为 1。例如: 5 ⊕ 6 = ( 101 ) 2 ⊕ ( 110 ) 2 = ( 011 ) 2 = 3 5 \oplus 6 = (101)_2\oplus(110)_2 = (011)_2= 3 5⊕6=(101)2⊕(110)2=(011)2=3
输入格式
第一行,输入一个正整数
n
n
n
(
1
≤
n
≤
1
×
1
0
5
)
(1\le n\le 1\times10^5)
(1≤n≤1×105),代表有序整数序列的长度。
第二行,输入
n
n
n 个整数
a
1
,
a
2
,
…
,
a
n
a_1, a_2, \dots, a_n
a1,a2,…,an
(
0
≤
a
i
≤
1
×
1
0
9
,
∑
a
≤
1
×
1
0
9
)
(0\le a_i\le 1\times10^9,\sum a\le 1\times10^9)
(0≤ai≤1×109,∑a≤1×109),表示有序整数序列。
第三行,输入一个正整数
q
q
q
(
1
≤
q
≤
1
×
1
0
5
)
(1\le q\le 1\times10^5)
(1≤q≤1×105),代表一共有
q
q
q 次查询。
接下来
q
q
q 行,每行两个正整数
l
,
r
l,r
l,r
(
1
≤
l
≤
r
≤
n
)
(1\le l\le r \le n)
(1≤l≤r≤n),查询区间
[
l
,
r
]
[l,r]
[l,r]的魔法值。
输出格式
输出共 q q q 行,每行输出一个整数,代表每次查询的结果。
输入 #1
4
0 1 2 1
3
1 4
1 3
1 1
输出 #1
5
5
1
样例解释:
对于第一次查询,
S
1
,
4
=
{
0
,
1
,
2
,
1
}
,
S_{1, 4} = \{ 0,1,2,1 \},
S1,4={0,1,2,1},其子集有
{
0
}
,
{
1
}
,
{
2
}
,
{
0
,
1
}
,
{
0
,
2
}
,
{
1
,
1
}
,
{
1
,
2
}
,
\{0\},\{1\},\{2\},\{0,1\},\{0,2\},\{1,1\}, \{1,2\},
{0},{1},{2},{0,1},{0,2},{1,1},{1,2},
{
0
,
1
,
2
}
,
{
1
,
1
,
2
}
,
{
0
,
1
,
1
,
2
}
.
\{0,1,2\},\{1,1,2\},\{0,1,1,2\}.
{0,1,2},{1,1,2},{0,1,1,2}.
故而
T
1
,
4
=
{
0
,
1
,
2
,
3
,
4
}
,
T_{1, 4} = \{ 0,1,2,3 ,4\},
T1,4={0,1,2,3,4},一种最优的子集选择方案是
R
=
{
0
,
4
}
,
R = \{0,4\},
R={0,4},此时
mex
(
{
0
,
4
}
)
⊕
gcd
(
{
0
,
4
}
)
=
1
⊕
4
=
5.
\operatorname{mex}(\{0,4\}) \oplus \gcd(\{0,4\}) = 1\oplus4=5.
mex({0,4})⊕gcd({0,4})=1⊕4=5.
对于第二次查询, S 1 , 3 = { 0 , 1 , 2 } , S_{1, 3} = \{ 0,1,2 \}, S1,3={0,1,2}, T 1 , 3 = { 0 , 1 , 2 , 3 } , T_{1, 3} = \{ 0,1,2,3 \}, T1,3={0,1,2,3},一种最优的子集选择方案是 R = { 0 , 1 , 2 , 3 } , R = \{0,1,2,3\}, R={0,1,2,3},此时 mex ( { 0 , 1 , 2 , 3 } ) ⊕ gcd ( { 0 , 1 , 2 , 3 } ) = 4 ⊕ 1 = 5. \operatorname{mex}(\{0,1,2,3\}) \oplus \gcd(\{0,1,2,3\}) = 4\oplus1=5. mex({0,1,2,3})⊕gcd({0,1,2,3})=4⊕1=5.
对于第三次查询, S 1 , 1 = { 0 } , S_{1, 1} = \{ 0 \}, S1,1={0}, T 1 , 1 = { 0 } , T_{1, 1} = \{ 0\}, T1,1={0},一种最优的子集选择方案是 R = { 0 } , R = \{0\}, R={0},此时 mex ( { 0 } ) ⊕ gcd ( { 0 } ) = 1 ⊕ 0 = 1. \operatorname{mex}(\{0\}) \oplus \gcd(\{0\}) = 1\oplus0=1. mex({0})⊕gcd({0})=1⊕0=1.
输入 #2
3
0 2 4
4
1 3
1 1
2 2
3 3
输出 #2
7
1
2
4
题解
看题无从下手,我们不妨假设已知
T
ℓ
,
r
T_{\ell,r}
Tℓ,r,然后考虑
max
R
⊆
T
ℓ
,
r
R
≠
∅
(
mex
(
R
)
⊕
gcd
(
R
)
)
\max_{ \substack{R \subseteq T_{\ell, r} \\ R \neq \varnothing}} \big( \operatorname{mex}(R) \oplus \gcd(R) \big)
maxR⊆Tℓ,rR=∅(mex(R)⊕gcd(R)) 怎么求:
我们发现当
gcd
\operatorname{gcd}
gcd 很大的时候
mex
\operatorname{mex}
mex 很小,
mex
\operatorname{mex}
mex 很大的时候
gcd
\operatorname{gcd}
gcd 很小,当直接去求会很难,我们不妨枚举
mex
\operatorname{mex}
mex 去看
gcd
\operatorname{gcd}
gcd是怎么变化的。
当 mex = 0 \operatorname{mex}=0 mex=0 的时候,除了 0 0 0 的数我们都可以加入子集 R R R,此时我们怎么取 gcd \operatorname{gcd} gcd呢?我们发现直接取 T ℓ , r T_{\ell, r} Tℓ,r 的最大值 m x mx mx 可以让 gcd \operatorname{gcd} gcd 最大,故这种情况的最优结果为 m x mx mx。
当 mex = 1 \operatorname{mex}=1 mex=1 的时候,子集 R R R 中必须选取 0 0 0,看题中的定义,我们可以认为 0 0 0对 gcd \operatorname{gcd} gcd 是没有影响的,此时我们还是选取 T ℓ , r T_{\ell, r} Tℓ,r 的最大值 m x mx mx 加入子集即可,此时答案为 m x ⊕ 1 mx\oplus1 mx⊕1。我们发现,如果区间中有 1 1 1 的话,可能 ( m x − 1 ) ⊕ 1 > m x ⊕ 1 (mx-1)\oplus1\gt mx\oplus1 (mx−1)⊕1>mx⊕1,所以答案为 max ( m x ⊕ 1 , ( m x − 1 ) ⊕ 1 ) \operatorname{max}(mx\oplus1,(mx-1)\oplus1) max(mx⊕1,(mx−1)⊕1),实际 ( m x − 1 ) ⊕ 1 (mx-1)\oplus1 (mx−1)⊕1 是不需要考虑的的,因为在 m e x = 0 mex=0 mex=0 的答案 ( m x ⊕ 0 ) ≥ ( m x − 1 ) ⊕ 1 (mx\oplus 0)\ge (mx-1)\oplus1 (mx⊕0)≥(mx−1)⊕1。
当
mex
≥
2
\operatorname{mex}\ge2
mex≥2 的时候,子集
R
R
R 中必须选取
0
,
1
0,1
0,1,因为有
1
1
1的存在所以
gcd
\operatorname{gcd}
gcd 的值只能为
1
1
1,我们只需要知道最大的
mex
(
R
)
\operatorname{mex}(R)
mex(R) 即可,答案为
m
e
x
⊕
1
mex\oplus1
mex⊕1,注意到
(
m
e
x
−
1
)
⊕
1
(mex-1)\oplus1
(mex−1)⊕1可能更大,所以答案为
max
(
m
e
x
⊕
1
,
(
m
e
x
−
1
)
⊕
1
)
\operatorname{max}(mex\oplus1,(mex-1)\oplus1)
max(mex⊕1,(mex−1)⊕1),但实际上
(
m
e
x
−
1
)
⊕
1
(mex-1)\oplus1
(mex−1)⊕1也是不需要考虑的,因为
m
e
x
=
1
mex=1
mex=1 时,
m
x
⊕
1
≥
(
m
e
x
−
1
)
⊕
1
mx\oplus1\ge(mex-1)\oplus1
mx⊕1≥(mex−1)⊕1。
查询的答案就是上述三种情况的最大值。
m
x
mx
mx很好求,直接维护前缀和即可。
考虑没有区间查询时怎么由原序列求出
mex
(
R
)
\operatorname{mex}(R)
mex(R) 的最大值。
首先考虑有没有
0
0
0,没有那么
mex
\operatorname{mex}
mex值就是
0
0
0。 先对
a
a
a 序列进行排序。
本质上就是求序列
a
a
a 子集所能表示出来的从0开始的最大连续区间。
设当前我们能表示的数字区间是
[
0
,
x
]
[0,x]
[0,x],下一个要添加的数为
a
i
a_i
ai。
如果
a
i
≤
x
+
1
a_i \le x+1
ai≤x+1,填加
a
i
a_i
ai后能表示出来的区间为
[
0
,
x
+
a
i
]
[0,x+a_i ]
[0,x+ai]。
如果
a
i
>
x
+
1
a_i \gt x+1
ai>x+1,那么
x
+
1
x+1
x+1 这个数就表示不出来,所以答案为
x
+
1
x+1
x+1。
这样一次暴力询问的复杂度是
O
(
n
)
O(n)
O(n) 。
考虑怎么优化询问。设当前可表示的值域为
[
0
,
x
]
[0,x]
[0,x],则令
m
e
x
=
x
+
1
mex=x+1
mex=x+1。
若序列中小于等于
m
e
x
mex
mex 的数的和
r
e
s
≥
m
e
x
res\ge mex
res≥mex,则一定有未选的且小于等于
m
e
x
mex
mex 的数,此时我们让
m
e
x
=
r
e
s
+
1
mex=res+1
mex=res+1,然后继续这个过程。
反之说明答案就是
m
e
x
mex
mex,直接退出这个过程即可。
上面这个值域的扩展过程类似于斐波那契数列,所以复杂度是
O
(
l
o
g
(
max
(
a
i
)
)
)
O(log(\max (a_i) ))
O(log(max(ai))) 的。
如何查询一段区间内一个值域区间的和我们直接用可持久化线段树维护即可。可持久化线段树单次求和为
O
(
l
o
g
(
max
(
a
i
)
)
)
的
O(log(\operatorname{max}(a_i)))的
O(log(max(ai)))的 复杂度。所以总复杂度为
O
(
n
l
o
g
2
(
max
(
a
i
)
)
)
O(nlog^2(\max (a_i) ))
O(nlog2(max(ai)))。
题解代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5+10;
int tot;
int sum[N*32];
int root[N],ls[N*32],rs[N*32],a[N];
int sumall[N],sum1[N],sum0[N];
int n;
void pushup(int u){
sum[u]=sum[ls[u]]+sum[rs[u]];
}
void modify(int &u,int v,int l,int r,int p){
if(!u)u=++tot;
if(l==r){
sum[u]=sum[v]+p;
return ;
}
int mid = l+r>>1;
if(p<=mid){
rs[u]=rs[v];
modify(ls[u],ls[v],l,mid,p);
}
else{
ls[u]=ls[v];
modify(rs[u],rs[v],mid+1,r,p);
}
pushup(u);
}
ll query(int u,int v,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr){
return sum[u]-sum[v];
}
int mid = l+r>>1;
ll ret = 0;
if(mid>=ql)ret+=query(ls[u],ls[v],l,mid,ql,qr);
if(mid+1<=qr)ret+=query(rs[u],rs[v],mid+1,r,ql,qr);
return ret;
}
void solve(){
cin>>n;
int mx = 0;
for(int i=1;i<=n;i++){
cin>>a[i];
mx=max(mx,a[i]);
}
for(int i=1;i<=n;i++){
if(a[i])modify(root[i],root[i-1],1,mx,a[i]);
else root[i]=root[i-1];
sumall[i]=sumall[i-1]+a[i];
if(a[i]==1)sum1[i]++;
if(a[i]==0)sum0[i]++;
sum1[i]+=sum1[i-1];
sum0[i]+=sum0[i-1];
}
int q;
cin>>q;
while(q--){
int l,r;
cin>>l>>r;
//处理最大可能取到的mex值
ll mex = 0;
if(sum0[r]-sum0[l-1]){
mex = 1;
while(1){
ll tmp = query(root[r],root[l-1],1,mx,1,mex);
if(tmp>=mex)mex=tmp+1;
else break;
}
}
ll ans = 0;
//mex=0
ans = sumall[r]-sumall[l-1];
//mex=1
if(mex){
ll num1 = sum1[r]-sum1[l-1];
ll s = sumall[r]-sumall[l-1];
ll tmp = s^1;
//mex=0的情况>=(s-1)^1,这步可以没有
if(num1)tmp = max(tmp,(s-1)^1);
ans = max(ans,tmp);
}
//mex>1,gd=1
if(mex){
//mex=1时 s^1>=(mex-1)^1,这步也可以没有
ll tmp = max((mex-1)^1,mex^1);
ans = max(ans,tmp);
}
cout<<ans<<"\n";
}
}
int32_t main(){
ios::sync_with_stdio(0),cin.tie(0);
int _= 1;
// cin>>_;
while(_--)solve();
}
区间查询mex异或gcd优化
2222

被折叠的 条评论
为什么被折叠?



