1. 程式人生 > >Codeforces 893E Counting Arrays:dp + 線性篩 + 分解質因數 + 組合數結論

Codeforces 893E Counting Arrays:dp + 線性篩 + 分解質因數 + 組合數結論

tdi mem sizeof nlogn 表示 color esp span 整數

題目鏈接:http://codeforces.com/problemset/problem/893/E

題意:

  共q組數據(q <= 10^5),每組數據給定x,y(x,y <= 10^6)。

  問你有多少種長度為y,乘積為x的整數數列。(可以有負數)

題解:

  首先考慮數列只有正整數的情況。

  將x分解質因數:x = ∑ a[i]*p[i]

  由於x較大,所以要先用線性篩求出素數,再枚舉素數分解質因數。

  那麽一個乘積為x的數列可以看做,將x的所有∑ p[i]個質因子,分配到了y個位置上。

  設f(i)表示:將p[i]個質因子a[i],分配到y個位置上的方案數。

  所以乘積為x的數列總數ans = ∏ f(i)。

  其中,f(i)等價於:長度為y,和為p[i]的數列總數。

  由於是多組數據,所以要預處理出對於所有長度的f(i)。

  dp[i][j]表示y = i時,之和為j的數列總數。

  轉移:dp[i][j] = ∑ dp[i-1][0 to j]

  用前綴和優化轉移,總復雜度O(nlogn)。

  這樣就求出了只考慮正整數情況下的數列總數:ans = ∑ dp[y][p[i]]

  然後考慮加負號的情況。

  由於x為正數,所以只能加偶數個負號。

  所以加負號的方案數 = C(y,0) + C(y,2) + C(y,4) + ... + C(y,偶數)

  有一個組合數結論:∑ C(n,偶數) = ∑ C(n,奇數) = 2^(n-1)。

  所以最終ans = ans * (2^(y-1))即為最終答案。

AC Code:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #define MAX_X 1000005
 5 #define MAX_P 25
 6 #define SET_X 1000000
 7 #define SET_P 20
 8 #define MOD 1000000007
 9 
10
using namespace std; 11 12 int x,y,q; 13 int cnt,tot=0; 14 int p[MAX_P]; 15 int pw[MAX_X]; 16 int prime[MAX_X]; 17 int dp[MAX_X][MAX_P]; 18 int sum[MAX_X][MAX_P]; 19 bool mark[MAX_X]; 20 21 void cal_dp() 22 { 23 memset(dp,0,sizeof(dp)); 24 memset(sum,0,sizeof(sum)); 25 dp[0][0]=1; 26 for(int i=0;i<=SET_P;i++) sum[0][i]=1; 27 for(int i=1;i<=SET_X;i++) 28 { 29 for(int j=0;j<=SET_P;j++) 30 { 31 dp[i][j]=sum[i-1][j]; 32 sum[i][j]=(sum[i][j-1]+dp[i][j])%MOD; 33 } 34 } 35 } 36 37 void cal_pw() 38 { 39 pw[0]=1; 40 for(int i=1;i<=SET_X;i++) pw[i]=(pw[i-1]<<1)%MOD; 41 } 42 43 void sieve() 44 { 45 memset(mark,false,sizeof(mark)); 46 for(int i=2;i<=SET_X;i++) 47 { 48 if(!mark[i]) prime[++tot]=i; 49 for(int j=1;j<=tot && (long long)i*prime[j]<=SET_X;j++) 50 { 51 mark[i*prime[j]]=true; 52 if(!(i%prime[j])) break; 53 } 54 } 55 } 56 57 void resolve() 58 { 59 int t=x; 60 cnt=0; 61 memset(p,0,sizeof(p)); 62 for(int i=1;i<=tot && prime[i]*prime[i]<=x;i++) 63 { 64 if(t%prime[i]==0) 65 { 66 cnt++; 67 while(t%prime[i]==0) 68 { 69 t/=prime[i]; 70 p[cnt]++; 71 } 72 } 73 } 74 if(t!=1) p[++cnt]=1; 75 } 76 77 int cal_ans() 78 { 79 resolve(); 80 long long ans=1; 81 for(int i=1;i<=cnt;i++) ans=ans*dp[y][p[i]]%MOD; 82 return ans*pw[y-1]%MOD; 83 } 84 85 int main() 86 { 87 sieve(); 88 cal_dp(); 89 cal_pw(); 90 cin>>q; 91 while(q--) 92 { 93 cin>>x>>y; 94 cout<<cal_ans()<<endl; 95 } 96 }

Codeforces 893E Counting Arrays:dp + 線性篩 + 分解質因數 + 組合數結論