題目:
題意:
給出兩個長度為
n 的整數序列a[
0..n−1
],b[
0..n−1
]和非負整數
c 。
對於兩個長度為
n的整數序列,定義
∗ 運算,結果為乙個長度為
n的整數序列,例如f∗
g=h ,則有h[
k]=∑
i+j≡
k(modn)f
[i]⋅
g[j]
。 求a
∗b∗b
∗⋯∗b
每一位模(n
+1) 的值,其中有
c 個
∗運算,(n
+1) 是質數,
n 的質因數大小均不超過
10。 n≤
5⋅105
,a[i
],b[
i],c
≤109
題解:
由原根的性質可知,長度為
n 的fft即可支援
∗運算,難點在於bc
使得值域過大,即使能夠快速計算長度為
n 的fft,使用複數運算的fft也很難得到精確的答案。
先考慮如何快速計算長度為
n的fft。 當n
=2k 時,fft每次是將序列一分為二,然後利用分治的技巧來進行合併。
因此當n=2
k1⋅3
k2⋅5
k3⋅7
k4時,fft每次可能將序列一分為p(
p=2,
3,5,
7),合併時的式子需要重新推導。
不妨設是將
p 個長度為
n的式子合併成乙個長度為p⋅
n 的式子,即利用
p 組
n個點值得到pn
個點值。
由於**時將模
p 意義相同的部分放在了一起,所以對於合併後的多項式 f(
x)=∑
0≤iaixi
拆分的p
個多項式分別為 fr
(x)=
∑0≤i
ip+r
xi故有 f(ω
an+b
pn)=
∑0≤r
ωan+
bpn)
rfr(
ωbn)
於是可以o(
p)合併出每個點的值,而這樣的分治層數是o(
∑4i=
1ki)
=o(logn)
,每層的複雜度是o(
pn)=
o(7n
) ,因此整體的複雜度是o(
nlogn)
。 上述方法也可非遞迴實現,在**過程中注意每段之間互不影響,在合併過程中注意儲存方式即可,筆者的做法就是迭代的做法。
再考慮解決精度問題,由同餘關係的性質,可以使得每次計算相乘時的值域降低到o(
n2) ,但需要將單位復根對映到模意義下的剩餘系中。 由於(
n+1)
是質數,φ(
n+1)
=n,所以在模(n
+1) 意義下存在原根
g ,使得g≡
ωn(modn+
1),於是利用ntt代替fft計算即可。
由於模(n+
1)意義下原根的數量為φ(
n)=n
∏pii
spri
me,p
i|n1
−1pi
,而n 的質因子大小不超過10,所以期望檢查358
≈5上述做法基於
n 是10-smooth number
,即cooley–tukey fft algorithm,而對於更強性質的
n,可以使用bluestein』s algorithm。
**:
#include
#include
#include
using
namespace
std;
typedef
long
long ll;
const
int maxn = 500001;
int n, m, mod, tot, p[maxn], pw[maxn], a[maxn], b[maxn];
int mod_pow(int x, int k)
void ntt(int x[maxn], int flag)
; int *cur = x, *nxt = y;
for(int i = tot - 1, delta = n / p[i]; i > 0; --i, delta /= p[i], swap(cur, nxt))
for(int j = 0, *np = nxt; j < n; j += delta * p[i])
for(int k = 0; k < p[i]; ++k)
for(int l = 0, *cp = cur + j + k; l < delta; ++l, ++np, cp += p[i])
*np = *cp;
// recursion
for(int i = 0, clen = 1, nlen = p[i]; i < tot; ++i, clen = nlen, nlen *= p[i], swap(cur, nxt))
for(int j = 0, k = 0, ww = 1, delta = 0; j < n; ++j, k = k + 1
< clen ? k + 1 : 0, ww = (ll)ww * pw[i] % mod, delta = delta + nlen > j ? delta : delta + nlen)
if(flag == -1)
if(cur != x)
memcpy(x, cur, n * sizeof(int));
}int main()
}for(int i = 0; i < n; ++i)
scanf("%d", a + i);
ntt(a, 1);
for(int i = 0; i < n; ++i)
scanf("%d", b + i);
ntt(b, 1);
for(int i = 0; i < n; ++i)
a[i] = (ll)a[i] * mod_pow(b[i], m) % mod;
ntt(a, -1);
for(int i = 0; i < n; ++i)
printf("%d\n", a[i]);
return
0;}
bzoj 2306 Ctsc2011 幸福路徑
有向圖 g有n個頂點 1,2,n,點i 的權值為 w i 現在有乙隻螞蟻,從 給定的起點 v0出發,沿著圖 g 的邊爬行。開始時,它的體力為 1。每爬過一條邊,它的體力都 p,而螞蟻爬到某個頂點時的幸福度,是它當時的體力與該點權值的乘積。求最大幸福值。因為當體力很小後,對答案就沒什麼影響力,所以用乙...
BZOJ 5343 Ctsc2018 混合果汁
bzoj 5343 ctsc2018 混合果汁 二分答案 主席樹 題意 給出每個果汁的 p,美味度d,最多能放的體積l。定義果汁混合後的美味度為果汁的美味度的最小值。m次詢問,要求花費不大於g,總體積不小於l,求最大美味度,如果不能滿足,輸出 1。二分答案。然後轉變為求 前l小的果汁之和。類似任務查...
bzoj2306 Ctsc2011 幸福路徑
有向圖 g有n個頂點 1,2,n,點i 的權值為 w i 現在有乙隻螞蟻,從給定的起點 v0出發,沿著圖 g 的邊爬行。開始時,它的體力為 1。每爬過一條邊,它的體力都會下降為原來的 倍,其中 是乙個給定的小於1的正常數。而螞蟻爬到某個頂點時的幸福度,是它當時的體力與該點權值的乘積。我們把螞蟻在爬行...