Convolution | 2021牛客暑期多校训练营4 假设
x
,
y
x,y
x,y 的质因子分解为
x
=
∏
p
i
s
i
,
y
=
∏
p
i
t
i
x=\prod p_i^{s_i},y=\prod p_i^{t_i}
x=∏pisi,y=∏piti 我们定义运算符
x
⊗
y
=
∏
p
i
∣
s
i
−
t
i
∣
x\otimes y=\prod p_i^{|s_i-t_i|}
x⊗y=∏pi∣si−ti∣ 现在给你一个长度为
n
n
n 的序列
a
n
a_n
an 还有一个
c
c
c 现在已知:
b
i
=
∑
i
=
j
⊗
k
a
i
⋅
k
c
b_i=\sum_{i=j\otimes k} a_i\cdot k^c
bi=i=j⊗k∑ai⋅kc 其中
1
≤
i
,
j
,
k
≤
n
1\le i,j,k\le n
1≤i,j,k≤n ,后文省略 你需要求出每一个
b
i
b_i
bi (取模
998244353
998244353
998244353) ,输出他们的异或和即可。
1
≤
n
≤
1
0
6
1\le n\le 10^6
1≤n≤106
0
≤
a
i
<
998244353
0\le a_i<998244353
0≤ai<998244353
0
≤
c
≤
1
0
9
0\le c\le 10^9
0≤c≤109
题意
过程非常难想的推式子题目… 首先,我们直接枚举
i
,
j
,
k
i,j,k
i,j,k 当然会超时,考虑
⊗
\otimes
⊗ 运算符的本质 其实就是
x
⊗
y
=
l
c
m
(
x
,
y
)
gcd
(
x
,
y
)
=
x
⋅
y
gcd
(
x
,
y
)
2
x\otimes y=\frac{lcm(x,y)}{\gcd(x,y)}=\frac{x\cdot y}{\gcd(x,y)^2}
x⊗y=gcd(x,y)lcm(x,y)=gcd(x,y)2x⋅y 有
l
c
m
lcm
lcm 式子肯定不好搞,我们使用右边的式子。 所以现在就变成了:
b
i
=
∑
j
=
1
n
∑
k
=
1
n
[
j
⋅
k
gcd
(
j
,
k
)
2
=
i
]
×
a
j
×
k
c
b_i=\sum_{j=1}^n \sum_{k=1}^n \Big[ \frac{j\cdot k}{\gcd(j,k)^2}=i \Big] \times a_j \times k^c
bi=j=1∑nk=1∑n[gcd(j,k)2j⋅k=i]×aj×kc 很套路的,我们枚举
gcd
(
j
,
k
)
=
d
\gcd(j,k)=d
gcd(j,k)=d ,同时对称性构造
j
′
=
j
d
,
k
′
=
k
d
j^\prime =\frac{j}{d},k^\prime =\frac{k}{d}
j′=dj,k′=dk ,其中要求
j
′
⊥
k
′
j^\prime \perp k^\prime
j′⊥k′ 这个时候式子就变成了:
b
i
=
∑
j
=
1
n
∑
k
=
1
n
[
j
′
⋅
k
′
=
i
]
×
a
j
×
k
c
b_i=\sum_{j=1}^n \sum_{k=1}^n \Big[j^\prime \cdot k^\prime =i \Big] \times a_j \times k^c
bi=j=1∑nk=1∑n[j′⋅k′=i]×aj×kc 这么一看,就是枚举
i
i
i 的两个因子嘛! 我们转换枚举方式,先枚举一个因子
j
′
j^\prime
j′ ,再枚举其倍数
d
⋅
j
′
d\cdot j^\prime
d⋅j′ ,变成了:
b
i
=
∑
j
′
∣
i
∑
d
=
1
min
{
n
j
′
,
n
k
′
}
a
d
j
′
×
(
d
k
′
)
c
b_i=\sum_{j^\prime | i} \sum_{d=1}^{\min\{\frac{n}{j^\prime},\frac{n}{k^\prime} \}} a_{dj^\prime} \times (dk^\prime)^c
bi=j′∣i∑d=1∑min{j′n,k′n}adj′×(dk′)c 注意为什么
d
d
d 的上限中,分子是
n
n
n 不是
i
i
i 呢?因为
d
=
gcd
(
j
,
k
)
d=\gcd(j,k)
d=gcd(j,k),明显
1
≤
j
,
k
≤
n
1\le j,k\le n
1≤j,k≤n 注意式子中不用枚举
k
′
k^\prime
k′ ,因为此时
i
=
j
′
k
′
i=j^\prime k^\prime
i=j′k′ ,我们对于
i
i
i 只用枚举
j
′
j^\prime
j′ 就能获得
k
′
k^\prime
k′ 了 换句话说,
k
′
k^\prime
k′ 是与
d
d
d 无关的,我们令
d
d
d 的枚举上限为
u
p
up
up ,所以我们自然左移:
b
i
=
∑
j
′
∣
i
(
k
′
)
c
∑
d
=
1
u
p
a
d
j
′
×
d
c
b_i=\sum_{j^\prime | i}(k^\prime)^c \sum_{d=1}^{up} a_{dj^\prime} \times d^c
bi=j′∣i∑(k′)cd=1∑upadj′×dc 这样,复杂度我们变成了
O
(
n
2
n
)
O(n^2\sqrt n)
O(n2n) (假设我们提前预处理出快速幂
d
c
d^c
dc 的话)
注意到,对于同一个
i
i
i ,我们第二个求和的式子中,我们重算了许多东西(因为我们只会变
u
p
up
up 的值) 所以我们记一个辅助数组:
d
p
[
x
]
[
y
]
=
∑
d
=
1
y
a
d
x
×
d
c
dp[x][y]=\sum_{d=1}^y a_{dx}\times d^c
dp[x][y]=d=1∑yadx×dc 然后原来式子就变成了:
b
i
=
∑
j
′
∣
i
(
k
′
)
c
×
d
p
[
j
′
]
[
u
p
]
b_i=\sum_{j^\prime | i}(k^\prime)^c \times dp[j^\prime][up]
bi=j′∣i∑(k′)c×dp[j′][up] 注意到,我们从
d
p
[
j
′
]
[
x
]
dp[j^\prime][x]
dp[j′][x] 推到
d
p
[
j
′
]
[
x
+
1
]
dp[j^\prime][x+1]
dp[j′][x+1] 是非常好做的,值只增加了
a
j
′
(
x
+
1
)
⋅
(
x
+
1
)
c
a_{j^\prime(x+1)}\cdot (x+1)^c
aj′(x+1)⋅(x+1)c 我们还注意到,对于一个
j
′
j^\prime
j′ ,我们不会用到其他的
d
p
[
x
≠
j
′
]
[
y
]
dp[x\ne j^\prime][y]
dp[x=j′][y] ,所以我们可以省略成一维数组
d
p
[
u
p
]
dp[up]
dp[up] 由于
u
p
up
up 不超过
n
j
′
\frac{n}{j^\prime}
j′n ,所以我们获取所有
d
p
[
u
p
]
dp[up]
dp[up] 的复杂度是
O
(
n
log
n
)
O(n\log n)
O(nlogn) 的 所以我们现在,复杂度都在与求
b
i
b_i
bi 数组,复杂度为
O
(
n
n
)
O(n\sqrt n)
O(nn)
注意到,
b
i
b_i
bi 是求出
i
i
i 的一个因子
j
′
j^\prime
j′ 然后求和 我们当然可以先枚举因子
j
′
j^\prime
j′ ,再枚举倍数
k
′
k^\prime
k′ ,此时因子的倍数就是
j
′
⋅
k
′
=
i
j^\prime\cdot k^\prime=i
j′⋅k′=i,就是我们的每一个
i
i
i 了 这样复杂度就可以优化成
O
(
n
log
n
)
O(n\log n)
O(nlogn) 了!
代码
时间复杂度:
O
(
n
log
n
)
O(n\log n)
O(nlogn) 为什么分析这么复杂,代码还是这么短啊!
#include<bits/stdc++.h>#defineIOSios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;typedeflonglong ll;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}constint MAX =1e6+50;constint MOD =998244353;constint INF =0x3f3f3f3f;const ll LINF =0x3f3f3f3f3f3f3f3f;constdouble EPS =1e-5;
ll qpow(ll a,ll n){/* */ll res =1LL;while(n){if(n&1)res=res*a%MOD;a=a*a%MOD;n>>=1;}return res;}
ll qpow(ll a,ll n,ll p){a%=p;ll res =1LL;while(n){if(n&1)res=res*a%p;a=a*a%p;n>>=1;}return res;}
ll npow(ll a,ll n){/* */ll res =1LL;while(n){if(n&1)res=res*a;a=a*a;n>>=1;if(res<0||a<0)return0;}return res;}
ll inv(ll a){/* */returnqpow(a,MOD-2);}
ll inv(ll a,ll p){returnqpow(a,p-2,p);}
ll aa[MAX];
ll bb[MAX];
ll powc[MAX];
ll dp[MAX];intmain(){int n,c;n =read();c =read();for(int i =1;i <= n;++i)aa[i]=read_ll();for(int i =0;i <= n;++i)powc[i]=qpow(i,c);// 预处理出 i^cfor(int j2 =1;j2 <= n;++j2){for(int up =1;j2 * up <= n;++up){// 更新 dp[x] 数组
dp[up]=(dp[up-1]+ aa[j2 * up]* powc[up]% MOD)% MOD;}for(int k2 =1;j2 * k2 <= n;++k2){if(__gcd(j2,k2)!=1)continue;// 要求如此int i = j2 * k2;int up =min(n / j2,n / k2);
bb[i]=(bb[i]+ dp[up]* powc[k2]% MOD)% MOD;// 更新 b[x] 数组}}
ll ans =0;for(int i =1;i <= n;++i)ans ^= bb[i];Print(ans);Write();return0;}