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

Leohh發表於2018-02-27

題目連結: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 }

 

相關文章