https://www.luogu.org/problemnew/show/P3327
不会做。
去搜题解...为什么题解都用了一个奇怪的公式?太奇怪了啊。。。
公式是这样的:
$d(xy)=sum_{i|x}sum_{j|y}[(i,j)=1]$
证明:(转自:https://23613.blog.luogu.org/solution-p3327)
考虑一个质因子p,设x中p的指数为a,y中p的指数为b(指质因数分解结果中指数),那么根据因数个数定理,这个质因子对式子左边的贡献(指使得答案乘上的值)是a+b+1;在式子右边,要么i中p的指数为0且j中p的指数为1~b,要么j中p的指数为0且i中p的指数为1~a,要么i和j中p的指数均为0(为了保证i,j互质),所以p的贡献是(b+a+1)。那么左右两边就相等了...
没办法了,背公式吧...找不到别的做法
答案要求的式子ans可以化为$sum_{i=1}^nsum_{j=1}^m[(i,j)=1]{lfloor}frac{n}{i}{ floor}{lfloor}frac{m}{j}{ floor}$
反演一下,得到$ans=sum_{k=1}^mmu(k)sum_{i=1}^{{lfloor}frac{n}{k}{ floor}}sum_{j=1}^{{lfloor}frac{m}{k}{ floor}}{lfloor}frac{n}{ik}{ floor}{lfloor}frac{m}{jk}{ floor}$(不妨设n<=m)
设$f(x)=sum_{i=1}^x{lfloor}frac{x}{i}{ floor}$,这个显然可以n*sqrt预处理出来
$ans=sum_{k=1}^mmu(k)f({lfloor}frac{n}{k}{ floor})f({lfloor}frac{m}{k}{ floor})$
那么原式也可以简单的在一次询问sqrt的时间内求出
本以为就这样子A掉了,结果...T飞了
不管了。。O3开开A掉
网上找了一下,有一个方法卡常数:可以发现f函数是约数个数函数的前缀和,因此可以筛出约数个数然后直接前缀和,这样预处理就是O(n)的
1 #pragma GCC optimize(3) 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<vector> 6 using namespace std; 7 #define fi first 8 #define se second 9 #define mp make_pair 10 #define pb push_back 11 typedef long long ll; 12 typedef unsigned long long ull; 13 typedef pair<int,int> pii; 14 ll prime[20100],len,ff[50100],mu[50100]; 15 bool nprime[50100]; 16 ll ans,n,m; 17 int main() 18 { 19 ll i,j,k,T; 20 mu[1]=1; 21 for(i=2;i<=50000;i++) 22 { 23 if(!nprime[i]) prime[++len]=i,mu[i]=-1; 24 for(j=1;j<=len&&i*prime[j]<=50000;j++) 25 { 26 nprime[i*prime[j]]=1; 27 if(i%prime[j]==0) {mu[i*prime[j]]=0;break;} 28 else mu[i*prime[j]]=-mu[i]; 29 } 30 } 31 for(i=1;i<=50000;i++) mu[i]+=mu[i-1]; 32 for(k=1;k<=50000;k++) 33 { 34 for(i=1;i<=k;i=j+1) 35 { 36 j=min(k,k/(k/i)); 37 ff[k]+=(j-i+1)*(k/i); 38 } 39 } 40 //for(i=1;i<=10;i++) printf("%lld ",ff[i]); 41 scanf("%lld",&T); 42 while(T--) 43 { 44 scanf("%lld%lld",&n,&m); 45 if(n>m) swap(n,m); 46 ans=0; 47 for(i=1;i<=n;i=j+1) 48 { 49 j=min(n,min(n/(n/i),m/(m/i))); 50 ans+=(mu[j]-mu[i-1])*ff[n/i]*ff[m/i]; 51 } 52 printf("%lld ",ans); 53 } 54 return 0; 55 }
http://codeforces.com/contest/235/problem/E
是不是感觉非常像?
事实上,不仅式子像,结论也是一样的。。。好神奇啊
结论是:$d(xyz)=sum_{i|x}sum_{j|y}sum_{k|z}[(i,j)=1][(j,k)=1][(i,k)=1]$
证明也是类似的;貌似可以推广到更高维(?没试过)
同样的,$ans=sum_{i=1}^asum_{j=1}^bsum_{k=1}^c[(i,j)=1][(j,k)=1][(i,k)=1]{lfloor}frac{a}{i}{ floor}{lfloor}frac{b}{j}{ floor}{lfloor}frac{c}{k}{ floor}$
耶,我又不会了,反演嘛式子推不清楚,乱搞嘛搞不出来
到网上找,发现一个技巧:如果只是要求XXX=1,用$sum_{d|k}mu(d)=[k=1]$去代比直接反演更方便
有了这样一个做法:
枚举i,搞出所有可能的j,k(指与i互质且在范围内),分别放入数组aj,ak
那么就需要从aj,ak内分别取一个元素x,y,如果(x,y)==1则可以计算贡献
也就是要计算$sum_{j{in}aj}sum_{k{in}ak}[(j,k)=1]{lfloor}frac{b}{j}{ floor}{lfloor}frac{c}{k}{ floor}=sum_{j{in}aj}sum_{k{in}ak}sum_{d|(j,k)}mu(d){lfloor}frac{b}{j}{ floor}{lfloor}frac{c}{k}{ floor}$
考虑对每一个d统计贡献,复杂度n^2*log
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 using namespace std; 6 #define fi first 7 #define se second 8 #define mp make_pair 9 #define pb push_back 10 typedef long long ll; 11 typedef unsigned long long ull; 12 typedef pair<int,int> pii; 13 int prime[2010],len,mu[2010]; 14 bool nprime[2010]; 15 int a,b,c,mm; 16 unsigned ans,a1,n1,n2; 17 bool aj[2010],ak[2010]; 18 int main() 19 { 20 int i,j,k,d; 21 mu[1]=1; 22 for(i=2;i<=2000;i++) 23 { 24 if(!nprime[i]) prime[++len]=i,mu[i]=-1; 25 for(j=1;j<=len&&i*prime[j]<=2000;j++) 26 { 27 nprime[i*prime[j]]=1; 28 if(i%prime[j]==0) {mu[i*prime[j]]=0;break;} 29 else mu[i*prime[j]]=-mu[i]; 30 } 31 } 32 scanf("%d%d%d",&a,&b,&c); 33 mm=max({a,b,c}); 34 for(i=1;i<=a;i++) 35 { 36 a1=0; 37 for(j=1;j<=b;j++) 38 aj[j]=(__gcd(i,j)==1); 39 for(k=1;k<=c;k++) 40 ak[k]=(__gcd(i,k)==1); 41 for(d=1;d<=mm;d++) 42 { 43 n1=n2=0; 44 for(j=d;j<=b;j+=d) 45 n1+=aj[j]*(b/j); 46 for(k=d;k<=c;k+=d) 47 n2+=ak[k]*(c/k); 48 a1+=n1*n2*mu[d]; 49 } 50 ans+=a1*(a/i); 51 } 52 printf("%u",ans%1073741824); 53 return 0; 54 }