[bzoj3733]Iloczyn 题解(搜索剪枝)
3733: [Pa2013]Iloczyn
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 741 Solved: 217
[Submit][Status][Discuss]
Description
给定正整数n和k,问能否将n分解为k个不同正整数的乘积
Input
第一行一个数T(T<=4000)表示测试组数
接下来T行每行两个数n(n<=10^9),k(k<=20)
Output
输出T行,若可以被分解,输出"TAK"否则输出"NIE"
Sample Input
3
15 2
24 4
24 5
15 2
24 4
24 5
Sample Output
TAK
TAK
NIE
TAK
NIE
一开始还以为是什么神仙数论题,后来发现搜就完事了。
先预处理一下约数并排好序,设x表示可选x到cnt的所有因数,num表示已经选了num个,prod表示当前选取因数的乘积,然后一顿狂暴血怒滚键盘剪枝就水过了。
比较有效的一个是如果当前最小的num个可选因数的乘积都大于n就直接return false。
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int T,n,K; int fact[50005],cnt; void div(int val) { for(int i=1;i*i<=n;i++) if(n%i==0) { fact[++cnt]=i; if(i*i!=n)fact[++cnt]=n/i; } sort(fact+1,fact+cnt+1); } bool dfs(int x,int num,int prod) { if(num==K&&prod==n)return 1; if(num>=K)return 0; if(x==cnt+1)return 0; if(cnt-x+1<K-num)return 0; int now=prod; for(int i=x,j=num+1;i<=cnt&&j<=K;i++,j++) { if(1LL*now*fact[i]>n)return 0; else now=1LL*now*fact[i]; } if(dfs(x+1,num,prod))return 1; if(1LL*prod*fact[x]<=n&&dfs(x+1,num+1,prod*fact[x]))return 1; return 0; } void work() { scanf("%d%d",&n,&K); cnt=0; div(n); if(cnt<K){puts("NIE");return ;} if(dfs(1,0,1))puts("TAK"); else puts("NIE"); return ; } int main() { scanf("%d",&T); while(T--)work(); return 0; }
兴许青竹早凋,碧梧已僵,人事本难防。