【数学 技巧】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

原文地址:https://www.cnblogs.com/antiquality/p/10357448.html