2023牛客暑期多校训练营5-I The Yakumo Family
题目描述
给出长度为n
(
3
<
=
n
<
=
2
∗
1
0
5
)
(3<=n<=2*10^5)
(3<=n<=2∗105)的数
a
i
(
0
<
=
a
i
<
=
1
0
9
)
a_i(0<=a_i<=10^9)
ai(0<=ai<=109),
∑
1
≤
l
1
≤
r
1
≤
l
2
≤
r
2
≤
l
3
≤
r
3
≤
r
1
≤
n
\sum_{1\le l_1 \le r_1 \le l_2 \le r_2 \le l_3 \le r_3 \le r_1 \le n }
∑1≤l1≤r1≤l2≤r2≤l3≤r3≤r1≤n 求
X
O
R
(
l
1
,
r
1
)
∗
X
O
R
(
l
2
,
r
2
)
∗
X
O
R
(
l
3
,
r
3
)
XOR(l_1,r_1)*XOR(l_2,r_2)*XOR(l_3,r_3)
XOR(l1,r1)∗XOR(l2,r2)∗XOR(l3,r3)最后再模998244353。
其中
X
O
R
(
l
1
,
r
1
)
XOR(l_1,r_1)
XOR(l1,r1)表示
a
l
a_l
al^
a
l
+
1
a_{l+1}
al+1… ^
a
r
a_r
ar也就是区间[l,r]的异或和
思路
我们可以先用前缀和记录数组,用 b i = b i − 1 b_i=b_{i-1} bi=bi−1^ a i a_i ai 这样一个 X O R ( l 1 , r 1 ) XOR(l_1,r_1) XOR(l1,r1)就等与 b l 1 − 1 b_{l_1-1} bl1−1^ b r 1 b_{r_1} br1接下来求不同区间只需要从数组b来判断。
-
利用拆位,将其先转换成二进制的01之后再来进行XOR判断
-
我们确定右端点,来枚举左端点,若要XOR(l,r)有意义(有值)则拆位后异或要为1。
枚举拆位之后的每一位,若右端点为1则左端点必须选择0才能做出贡献。 -
设 p r e i , 1 / 0 pre_{i,1/0} prei,1/0表示前i个数中,1/0的个数
-
s u m i sum_i sumi 表示前i个数的总权值
(每一次拆位后的一位重新统计即5–1001,6–1010,我们统计
2
0
2^0
20位再
2
1
2^1
21位)
当然以上讨论的仅仅是只含
l
1
,
r
1
l_1,r_1
l1,r1的区间,而该题存在3个区间,所以只需要再加入一个维度k表示在左侧加入k个区间。而该题是顺序向下遍历,所以我们的pre和sum可以由上一个循环继承下去便也能省略。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
const int mod=998244353;
int n;
long long a[N];
long long sum[N];
long long b[N];
long long p[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
b[i]=a[i]^b[i-1];//前缀和记录
sum[i]=1;
}
for(int k=1;k<=3;k++)
{
for(int j=0;j<=30;j++)//拆位
{
long long cr[2]={0,0};//表示前i个1/0的个数
if(k==1) cr[0]=1;
for(int i=1;i<=n;i++)
{
p[i]=(p[i]+(cr[(((1<<j)&b[i])>>j)^1]<<j))%mod;//判断并加上该点以及左区间的贡献
cr[((1<<j)&b[i])>>j]=(cr[((1<<j)&b[i])>>j]+sum[i])%mod;
}
}
for(int i=1;i<=n;i++)
{
sum[i]=(sum[i-1]+p[i])%mod;
p[i]=0;
}
}
cout<<sum[n];
}