数学 技巧Problem A. divisor
Posted antiquality
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数学 技巧Problem A. divisor相关的知识,希望对你有一定的参考价值。
没考虑重复lcm处理被卡TLE没A真是可惜
题目大意
$n$为$k-可表达的$当且仅当数$n$能被表示成$n$的$k$个因子之和,其中$k$个因子允许相等。
求$[A,B]$之间$k-可表达$的数的个数
$T le 5*10^4,2 le K le 7,1 le A le B le 10^{18}$
题目分析
每一种拆分可以视作$1=frac{1}{a}+frac{1}{b}+...$的形式。因为K相当小,可以先搜出每个$K$下的拆分情况,问题就转化成了求$[l,r]$之间有多少数至少是其中一个的倍数——这是一个相当经典的问题。
但是这题的数据范围要求进一步细节优化。
注意到在极限数据$K=7,T=5*10^4$下,纯粹地$2^{因数}$枚举每一种情况lcm的做法相当低效。通过观察/经验会发现,这$2^{因数}$个lcm有大量是重复的。那么我们就转而保存每个lcm的系数而非把lcm全部存下来。
这是一个计算倍数容斥问题时,显著而不甚易想起的优化。
代码很丑。没心情重构。
1 #include<bits/stdc++.h> 2 const int ass5[7] = {0, 5, 6, 8, 9, 14, 21}; 3 const int ass6[7] = {0, 6, 8, 10, 14, 44, 52}; 4 const int ass7[16] = {0, 7, 8, 9, 10, 12, 15, 22, 33, 39, 52, 55, 68, 102, 114, 138}; 5 typedef long long ll; 6 7 int T; 8 ll l,r,k,gmpAss5[40035],gmpAss6[40035],gmpAss7[40035]; 9 10 ll read() 11 { 12 char ch = getchar(); 13 ll num = 0, fl = 1; 14 for (; !isdigit(ch); ch = getchar()) 15 if (ch==‘-‘) fl = -1; 16 for (; isdigit(ch); ch = getchar()) 17 num = (num<<1)+(num<<3)+ch-48; 18 return num*fl; 19 } 20 int gcd(int a, int b){return !b?a:gcd(b, a%b);} 21 ll assp5(ll x) 22 { 23 ll ret = 0; 24 for (int i=1; i<64; i++) 25 ret += x/gmpAss5[i]; 26 return ret; 27 } 28 ll assp6(ll x) 29 { 30 ll ret = 0; 31 for (int i=1; i<64; i++) 32 ret += x/gmpAss6[i]; 33 return ret; 34 } 35 ll assp7(ll x) 36 { 37 ll ret = 0; 38 for (int i=1; i<=gmpAss7[0]; i++) 39 if (gmpAss7[i]) ret += x/gmpAss7[i]; 40 return ret; 41 } 42 void makeAss5() 43 { 44 for (int i=1, ass=6; i<(1<<(ass)); i++) 45 { 46 ll dt = 0, lst = 1, del; 47 for (int j=1, t=i; j<=ass; j++, t>>=1) 48 if (t&1){ 49 ++dt, del = gcd(lst, ass5[j]); 50 lst *= ass5[j]/del; 51 } 52 gmpAss5[i] = (dt&1)?lst:-lst; 53 } 54 } 55 void makeAss6() 56 { 57 for (int i=1, ass=6; i<(1<<(ass)); i++) 58 { 59 ll dt = 0, lst = 1, del; 60 for (int j=1, t=i; j<=ass; j++, t>>=1) 61 if (t&1){ 62 ++dt, del = gcd(lst, ass6[j]); 63 lst *= ass6[j]/del; 64 } 65 gmpAss6[i] = (dt&1)?lst:-lst; 66 } 67 } 68 void makeAss7() 69 { 70 ll &tot = gmpAss7[0]; 71 for (int i=1, ass=15; i<(1<<(ass)); i++) 72 { 73 ll dt = 0, lst = 1, del; 74 for (int j=1, t=i; j<=ass; j++, t>>=1) 75 if (t&1){ 76 ++dt, del = gcd(lst, ass7[j]); 77 lst *= ass7[j]/del; 78 } 79 lst = (dt&1)?lst:-lst; 80 bool chk = 1; 81 for (int i=1; i<=tot&&chk; i++) 82 if (gmpAss7[i]==-lst) gmpAss7[i] = 0, chk = 0; 83 if (chk) gmpAss7[++tot] = lst; 84 } 85 } 86 ll calc(ll x, ll k) 87 { 88 ll ret = 0; 89 if (k==1) return x; 90 if (k==2) return x>>1; 91 if (k==3) return (x/3+x/4-x/12); 92 if (k==4) return (x/4+x/6+x/10-x/12-x/30-x/20+x/60); 93 if (k==5) return assp5(x); 94 if (k==6) return assp6(x); 95 if (k==7) return assp7(x); 96 return ret; 97 } 98 void write(ll x){if (x/10) write(x/10);putchar(x%10+‘0‘);} 99 int main() 100 { 101 makeAss5(), makeAss6(), makeAss7(); 102 for (scanf("%d",&T); T; --T) 103 { 104 l = read(), r = read(), k = read(); 105 write(calc(r, k)-calc(l-1, k)), putchar(‘ ‘); 106 } 107 return 0; 108 }
END
以上是关于数学 技巧Problem A. divisor的主要内容,如果未能解决你的问题,请参考以下文章
计蒜客 ACM-ICPC 2018 南京赛区网络预赛 A. An Olympian Math Problem-数学公式题
hdu-2256 Problem of Precision---矩阵快速幂+数学技巧