本文屬於一個總結了一堆作法的玩意......html
簡單的一個式子:給定$n,k,f(i)$,求git
而後數據範圍不重要,重要的是如何優化這個作法。算法
這個式子有$n$種問法,並且能夠變式擴展,因此說這個式子也是比較重要的:數組
咱們約定若是給定了$n,k$那麼咱們的$g$寫做$g_k(n)$,若是給定了$n,k$中間的任意一個,枚舉另外一個,或者另外一個是變化的,那麼另外一個數記爲$i,j$函數
下面算法單詞回答部分有些因爲模數過大而要動態求逆元的緣由須要多個$log$優化
爲了區分我枚舉的$k$和題目中的$k$,這裏咱們約定題目中的$k$寫做大寫的$K$spa
比較大衆的算法,就是根據狄利克雷卷積($(f*g)(n)=\sum_{d|n}{f(d)*g(\frac{n}{d})}=\sum_{a*b=n}{f(a)*g{b}}$(這個$f,g$與題目無關))。code
咱們首先令$k=1$,而後$g_1(i)=\sum_{i_1|i}{f(i_1)}=(f*h)(n)$,這個時候咱們推一下知道$h(i)=1$了htm
同時$k=2$時$g_2(i)=(f*h*h)(n)=(f*h^2)(n)$blog
答案就是$(f*(h=1)^K)(i)$
因爲狄利克雷卷積知足結合律,因此能夠用快速冪優化,每次計算狄利克雷卷積複雜度$O(nlog_2n)$,快速冪$O(log_2n)$
代碼(早期):
1 #include <cstdio> 2 #include <cctype> 3 #include <cstring> 4 using namespace std; 5 6 #define MAX_BUF 10000000 7 #define MAXN 100005 8 #define MODS 1000000007 9 #define LL long long 10 11 struct FR{ 12 char buf[MAX_BUF + 1], *p1, *p2; 13 FR(){ 14 p2 = (p1 = buf) + fread(buf, 1, MAX_BUF, stdin); 15 } 16 inline void flash(){ 17 p2 = (p1 = buf) + fread(buf, 1, MAX_BUF, stdin); 18 } 19 inline char get_char(){ 20 return p1 == p2 ? EOF : *p1++; 21 } 22 }fr; 23 24 inline LL read(){ 25 LL num = 0; 26 char c, sf = 1; 27 while(isspace(c = fr.get_char())); 28 if(c == '-') c = fr.get_char(), sf = -1; 29 while(num = num * 10 + c - 48, isdigit(c = fr.get_char())); 30 return num * sf; 31 } 32 33 LL n, k, f[MAXN], g[MAXN], h[MAXN], tmp[MAXN]; 34 35 inline void Dirchlet(LL *scr, LL *dst){ 36 memset(tmp, 0, sizeof(tmp)); 37 for(int i = 1, j; i * i <= n; i++) 38 for((tmp[i * i] += scr[i] * dst[i] % MODS) %= MODS, j = i + 1; i * j <= n; j++) 39 (tmp[i * j] += scr[i] * dst[j] % MODS + scr[j] * dst[i] % MODS) %= MODS; 40 memcpy(dst, tmp, sizeof(tmp)); 41 } 42 43 int main(){ 44 LL T = read(); 45 while(T--){ 46 n = read(), k = read(); 47 for(LL i = 1; i <= n; i++) 48 f[i] = read(), h[i] = 0, g[i] = 1; 49 h[1] = 1; 50 while(k){ 51 if(k & 1) Dirchlet(g, h); 52 Dirchlet(g, g); 53 k >>= 1; 54 } 55 Dirchlet(f, h); 56 for(LL i = 1; i <= n; i++) printf("%lld%c", h[i], i == n ? '\n' : ' '); 57 } 58 return 0; 59 }
之前看的:複雜度$O(nlog_2^2n)$(<--原文),之前拿這個水過題,例如快速求單個$g_k(n)$的時候效果比較好。
思路大概就是dp出因子的貢獻而後計算出組合,即某個因子在全部可能傳遞狀況下是如何傳遞的。
解:設$dp[i][j]$表示在$n=i$的時候,中間的$\Sigma$部分選擇了$j$個因子(原文是不一樣的因子,可是我並不理解)時對於第$i$個位置所產生的貢獻。
因此$dp[i][k] += dp[j][k - 1](j|i,k \leq log(n),dp[i][0] = f[i])$,舉個栗子:在最後一個$\Sigma$(第k層)的位置$i_{k}|i_{k-1}$,而第k-1層枚舉因子的時候就會有一個$x * i_{k} = i_{k - 1}$,這個貢獻最終是計入到了$k-1$上去的。可是從$f(j)$到$g(i)$中間的$\Sigma$在不一樣的位置消掉了$r$次因子不止一種方案,$f(j)$不止作了一次貢獻,這個方案數就是$C(K,r)$。
通俗地說:咱們求$g(i)$,可是這個$g(i)$是由多個$f(j)$湊出來的,如何將$g(i)$和$f(j)$扯上關係?就是中間$k$個$\Sigma$有些$\Sigma$會吃掉一些因子,假設$K$個$\Sigma$吃了$r$個因子,那麼也就是說有些$\Sigma$吃了個人因子,而有些沒有,這些狀況都會致使$f(j)$對$g(i)$作貢獻。所以若是如今我知道個人因子一共被$K$個$\Sigma$吃了$r$個的話那麼就至關於$K$個物品中選出$r$個物品,方案數$C(K,r)$。
也就是$g_K(n)=C(K,j)*dp[n][j](j \leq log(n))$
可是時間複雜度都會有瓶頸(dp是$O(nlog_2^2n)$,單詞詢問$O(log_2^2n)$),不過適用性很好。
代碼(早期):
1 #include <cstdio> 2 #include <cctype> 3 #include <cstring> 4 using namespace std; 5 6 #define MAX_BUF 10000000 7 #define MAXK 25 8 #define MAXN 100005 9 #define MODS 1000000007 10 #define LL long long 11 12 struct FR{ 13 char buf[MAX_BUF + 1], *p1, *p2; 14 FR(){ 15 p2 = (p1 = buf) + fread(buf, 1, MAX_BUF, stdin); 16 } 17 inline void flash(){ 18 p2 = (p1 = buf) + fread(buf, 1, MAX_BUF, stdin); 19 } 20 inline char get_char(){ 21 return p1 == p2 ? EOF : *p1++; 22 } 23 }fr; 24 25 inline LL read(){ 26 LL num = 0; 27 char c, sf = 1; 28 while(isspace(c = fr.get_char())); 29 if(c == '-') c = fr.get_char(), sf = -1; 30 while(num = num * 10 + c - 48, isdigit(c = fr.get_char())); 31 return num * sf; 32 } 33 34 LL n, k, f[MAXN], c[MAXN][MAXK], dp[MAXN][MAXK], fac[MAXN]; 35 36 inline LL Get_Pow(LL base, LL t){ 37 LL ret = 1; 38 while(t){ 39 if(t & 1) (ret *= base) %= MODS; 40 (base *= base) %= MODS; 41 t >>= 1; 42 } 43 return ret; 44 } 45 46 inline LL Get_C(LL a, LL b){ 47 if(c[a][b]) return c[a][b]; 48 LL u = fac[a], d = fac[a - b] * fac[b] % MODS; 49 return u * Get_Pow(d, MODS - 2) % MODS; 50 } 51 52 const LL Up_K = 21; 53 54 int main(){ 55 LL T = read(); 56 fac[0] = 1; 57 for(int i = 1; i < MAXN; i++) fac[i] = fac[i - 1] * i % MODS; 58 while(T--){ 59 memset(dp, 0, sizeof(dp)); 60 n = read(), k = read(); 61 for(LL i = 1, j; i <= n; i++) 62 for(dp[i][0] = f[i] = read(), j = i << 1; j <= n; j += i) 63 for(LL r = 0; r < Up_K; r++) 64 (dp[j][r + 1] += dp[i][r]) %= MODS; 65 for(LL i = 1; i <= n; i++){ 66 LL ans = 0; 67 for(LL r = 0; r < Up_K; r++) 68 (ans += Get_C(k, r) * dp[i][r] % MODS) %= MODS; 69 printf("%lld%c", ans, i == n ? '\n' : ' '); 70 } 71 } 72 return 0; 73 }
這個我看不懂它的定義,可是乘法同構的思路好像和下面差很少,可是時間複雜度差了一些,可是這裏貼出原來的題和題解:對於上面式子在$n,k<=1e5$下詢問$1e3$次(能夠用dp+組合數學作)。
定義乘法同構: $A=p[1]^{a[1]} * p[2]^{a[2]} * ... * p[n]^{a[n]}$ $B=q[1]^{b[1]} * q[2]^{b[2]} * ... * q[n]^{b[n]}$ 其中p[i]與q[i]皆爲質數 將數組a與b降序排序後若是是徹底相同的,那麼稱A與B是乘法同構的 如 2*2*2*2*3*3*5 與 7*11*11*3*3*3*3 同構 咱們發現,10^5內在乘法同構下本質不一樣的數字只有165個 定義kind[x]:x在乘法同構下所屬於的種類 對着165個本質不一樣的數構造出矩陣A 咱們發現,$f_0[d]$對$f_k[x]$(d是x的因子)的貢獻爲$f_0[d] * A^k[kind[1]][kind[x/d]]$ 因此咱們只須要A^k[kind[1]][]這一行就夠了 預處理$A,A^2,A^4,A^8,...O(165^3*log(n))$ 對於每次詢問由於最終只須要一行,能夠優化到$O(165^2*log(n))$ 總複雜度$O(165^3*log(n) + m*165^2*log(n))$
這個最勁$O(nlog_2n)$,而後也能夠解決大部分問題,預處理$O(nlog_2n)$以及單次回答$O(log_2n)$,從HJWJBSR蒯來的.
並且目前在HDU上只有46ms(register優化居然失效了,速度還慢一些)。
思路:
咱們按照上一個dp+組合數學的思路,咱們設$h(j)$表示$f(i)$對$g(i*j)$的貢獻,若是算出了$h(1)~h(n)$的取值,那麼就能夠枚舉因數在$O(nlog_2n)$的時間內算出$f(1)~f(n)$。
如何計算$h(i)$:咱們首先發現$h(i)$只和$K$和$i*j$的因子有關,同時發現假設咱們咱們須要求$g(k_1^a)$和$g(k_2^a)$,在$K$同樣的狀況下,咱們傳遞因子的方案是相同的,也就是之前的$h(k^a)=C(a+K-1,a)$(上面一句話的$k,k_1,k_2$都是質因數)!而後因爲每一個因子傳遞的時候相互獨立!因此根據乘法原理,假設對要求的$h(n)$的$n$分解爲$n = k_1^{a_1}*k_2^{a_2}...k_x^{a_x}$,而後$h(n)=h(k_1^{a_1}*k_2^{a_2}...k_x^{a_x})=h(k_1^{a_1})*...*h(k_x^{a_x})$。咱們發現這是一個積性函數!能夠用線性篩求出來,因爲中間須要求某個數是某個質數的多少次冪,因此帶log。
代碼以下:
1 #include <cstdio> 2 #include <cctype> 3 #include <cstring> 4 5 //User's Lib 6 7 using namespace std; 8 9 char buf[11111111], *pc = buf; 10 char outp[1111111], *op = outp; 11 12 extern inline void Main_Init(){ 13 static bool INITED = false; 14 if(INITED){ 15 fwrite(outp, 1, op - outp, stdout); 16 fclose(stdin), fclose(stdout); 17 } else { 18 fread(buf, 1, 11111111, stdin); 19 INITED = true; 20 } 21 } 22 23 static inline int read(){ 24 int num = 0; 25 char c; 26 while((c = *pc ++) < 48); 27 while(num = num * 10 + c - 48, (c = *pc ++) >= 48); 28 return num; 29 } 30 31 inline void Write(const int &tar){//You Need To Write '-' and '\n' By Hand 32 if(!tar) return ; 33 Write(tar / 10); 34 *op ++ = (tar % 10) ^ 48; 35 } 36 37 namespace LKF{ 38 template <typename T> 39 extern inline T abs(T tar){ 40 return tar < 0 ? -tar : tar; 41 } 42 template <typename T> 43 extern inline void swap(T &a, T &b){ 44 T t = a; 45 a = b; 46 b = t; 47 } 48 template <typename T> 49 extern inline void upmax(T &x, const T &y){ 50 if(x < y) x = y; 51 } 52 template <typename T> 53 extern inline void upmin(T &x, const T &y){ 54 if(x > y) x = y; 55 } 56 template <typename T> 57 extern inline T max(T a, T b){ 58 return a > b ? a : b; 59 } 60 template <typename T> 61 extern inline T min(T a, T b){ 62 return a < b ? a : b; 63 } 64 } 65 66 //Source Code 67 68 const int MAXN = 100023; 69 const int MAXK = 18; 70 const int MODS = 1000000007; 71 72 int n, k; 73 int fac[MAXN]; 74 int f[MAXN], h[MAXN], g[MAXN]; 75 76 inline int Get_Pow(int base, int times){ 77 int ret = 1; 78 while(times){ 79 if(times & 1) ret = 1ll * ret * base % MODS; 80 base = 1ll * base * base % MODS; 81 times >>= 1; 82 } 83 return ret; 84 } 85 86 inline int Get_Inv(const int &tar){ 87 return Get_Pow(tar, MODS - 2); 88 } 89 90 int tot; 91 int prime[MAXN]; 92 bool is_prime[MAXN]; 93 94 inline void Get_Prime(){ 95 for(int i = 2; i < MAXN; i++){ 96 if(!is_prime[i]) prime[++ tot] = i; 97 for(int j = 1; j <= tot; j++){ 98 int tmp = i * prime[j]; 99 if(tmp >= MAXN) break; 100 is_prime[tmp] = true; 101 if(i % prime[j] == 0) break; 102 } 103 } 104 } 105 106 inline void Get_Fac(){ 107 fac[0] = 1; 108 for(int i = 1; i < MAXN; i++) 109 fac[i] = 1ll * fac[i - 1] * i % MODS; 110 } 111 112 int c[MAXK]; 113 114 inline int Get_C(const int &a, const int &b){//a > b 115 int tmp = 1ll * fac[a - b] * fac[b] % MODS; 116 return 1ll * fac[a] * Get_Inv(tmp) % MODS; 117 } 118 119 inline void Get_C(const int &k){ 120 for(int i = 1; i < MAXK; i++) 121 c[i] = Get_C(i + k, i); 122 } 123 124 inline void Get_H(){ 125 int cnt = 0; 126 h[1] = 1; 127 for(int i = 2; i <= n; i++){ 128 if(!is_prime[i]) h[i] = c[1], cnt ++; 129 for(int j = 1; j <= cnt; j++){ 130 int tmp = i * prime[j]; 131 if(tmp > n) break; 132 if(i % prime[j]) h[tmp] = 1ll * h[i] * h[prime[j]] % MODS; 133 else { 134 int Cnt = 0; 135 while(!(tmp % prime[j])) tmp /= prime[j], Cnt ++;//計算冪次 136 h[i * prime[j]] = 1ll * h[tmp] * c[Cnt] % MODS; 137 break; 138 } 139 } 140 } 141 } 142 143 int main(){ 144 Main_Init(); 145 Get_Fac(); 146 Get_Prime(); 147 int T = read(); 148 while(T --){ 149 memset(g, 0, sizeof(int) * (n + 5)); 150 n = read(), k = read() - 1; 151 for(int i = 1; i <= n; i++)//O(n) 152 f[i] = read(); 153 Get_C(k);//O(log^2) 154 Get_H();//O(nlogn) 155 for(int i = 1; i <= n; i++)//O(n + n / 2 + ...) = O(nlogn)(?) 156 for(int j = 1; i * j <= n; j++) 157 (g[i * j] += 1ll * f[i] * h[j] % MODS) %= MODS; 158 for(int i = 1; i <= n; i++){ 159 if(g[i]) Write(g[i]); 160 else *op ++ = '0'; 161 *op ++ = (i == n ? '\n' : ' '); 162 } 163 } 164 Main_Init(); 165 return 0; 166 }