1. 程式人生 > >【NOIP2017提高A組衝刺11.9】乘積

【NOIP2017提高A組衝刺11.9】乘積

Description

豆豆最近在潛心研究數學,他發現了一類很有趣的數字,叫做無平方因子數。也就是這一類數字不能夠被任意一個質數的平方整除,比如6、7、10都是無平方因子數,而12則不是。
所以豆豆在思考一個問題——選擇不超過K個N以內的正整數乘起來,使得乘積是一個無平方因子數,有多少種取法?(每個數只能取一次)

Input

第一行一個整數T表示資料組數。
接下來T行,每行兩個整數N,K,意思如題面所述。

Output

對於每一組資料,輸出一個整數表示取法的方案數對109+7取模後的數值。

Sample Input

3
1 1
6 4
4 2

Sample Output

1
19
6

Data Constraint

對於10%的資料:N≤8;
對於40%的資料:N≤16;
對於70%的資料:N≤30;
對於100%的資料:1≤T≤5;1≤K≤N≤500。

思路

70%
方法一:dfs。先篩出質數,然後分解質因數。設f[i]為恰好選i個的方案(不包括1)。搜尋得出f。ans=sigma(f(n-1))*2+f[0]+f[k];

方法二:統計 N 以內的質數有多少個,狀態壓縮到一個數 mask 當中,DP[i][k][mask]
表示前 i 個數選擇了 k 個數,現在含有 mask 中這些因數。
複雜度 O(2^10*N)

100%

狀壓 DP+分組揹包。
每一個數只會含有一個比 sqrt(N)大的質因數,所以我們把所有數字按照含有
比 sqrt(n)大的數分組,
sqrt(n)以內的質數只有 8 個。
然後 DP[i][j][mask]表示計算完了前 i 組數,選擇了 j 個數字,mask 表示當
前乘積含有比 sqrt(n)小的質因數的集合。
然後進行揹包就好了。

100%程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<memory.h>
#define maxn 507
using namespace std;
const int zs[8]={2,3,5,7,11,13,17,19};
int a[maxn],tt,n,k,f[maxn][1<<8],b[maxn][maxn];
int main()
{
    freopen("mul.in","r",stdin);
    freopen("mul.out"
,"w",stdout); for(int i=1; i<=500; i++) { int t=i; for(int j=0; j<8; j++) { if(t%zs[j]==0) a[i]+=1<<j,t/=zs[j]; if(t%zs[j]==0) { a[i]=-1; break; } } } scanf("%d",&tt); while(tt--) { memset(b,0,sizeof(b)); memset(f,0,sizeof(f)); scanf("%d%d",&n,&k); for(int i=1; i<=n; i++) { if(a[i]==-1) continue; int t=i; for(int j=0; j<8; j++) if(t%zs[j]==0) t/=zs[j]; if(t!=1) b[t][++b[t][0]]=i; else b[i][++b[i][0]]=i; } f[0][0]=1; for(int i=1; i<=n; i++) if(b[i][0]) for(int j=k-1; j>=0; j--) for(int l=1; l<=b[i][0]; l++) for(int zy=0,v=a[b[i][l]]; zy<(1<<8); zy++) if(!(zy&v)) f[j+1][zy+v]=(f[j+1][zy+v]+f[j][zy])%1000000007; long long ans=0; for(int i=1; i<=k; i++) for(int zy=0; zy<(1<<8); zy++) ans=(ans+f[i][zy])%1000000007; cout<<ans<<endl; } }