BZOJ 1919 Ctsc2010 效能優化

2021-07-13 21:09:30 字數 3301 閱讀 5801

題目:

題意:

給出兩個長度為

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的正常數。而螞蟻爬到某個頂點時的幸福度,是它當時的體力與該點權值的乘積。我們把螞蟻在爬行...