【BZOJ2154】Crash的数字表格
算是学会反演了……(其实挺好学的一天就能学会……
原题:
今天的数学课上,Crash小朋友学习了最小公倍数(Least Common Multiple)。对于两个正整数a和b,LCM(a, b)表示能同时被a和b整除的最小正整数。例如,LCM(6, 8) = 24。回到家后,Crash还在想着课上学的东西,为了研究最小公倍数,他画了一张N*M的表格。每个格子里写了一个数字,其中第i行第j列的那个格子里写着数为LCM(i, j)。一个4*5的表格如下: 1 2 3 4 5 2 2 6 4 10 3 6 3 12 15 4 4 12 4 20 看着这个表格,Crash想到了很多可以思考的问题。不过他最想解决的问题却是一个十分简单的问题:这个表格中所有数的和是多少。当N和M很大时,Crash就束手无策了,因此他找到了聪明的你用程序帮他解决这个问题。由于最终结果可能会很大,Crash只想知道表格里所有数的和mod 20101009的值。
反演嘛,直接推公式
(Atom和即时预览的latex插件真好用
(治好了我多年的公式恐惧症~~(模仿po姐
然后按照莫比乌斯反演经典的计算方法for(int i=1,j;i<=n;i=j+1) j=min(n/(n/i),m/(m/i));O(√n*√n)=O(n)计算就可以了
需要注意的是因为计算过程中是在模意义下计算的,所以会出现负数(但是因为计算是在模意义下进行的所以答案确实是对的),最后需要加模数再取模
(反演其实挺好学的一天就能学会(就学个反演都拖了一年我以前真是钍氧钍砷钋熵钛镎铱钨
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 #define ll long long 8 const int mo=20101009; 9 int rd(){int z=0,mk=1; char ch=getchar(); 10 while(ch<'0'||ch>'9'){if(ch=='-')mk=-1; ch=getchar();} 11 while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0'; ch=getchar();} 12 return z*mk; 13 } 14 void wt(int x){if(x<0) putchar('-'),x=-x; 15 int wtp=0; char wtc[22]; 16 while(x) wtc[++wtp]=(x%10)+'0',x/=10; 17 while(wtp) putchar(wtc[wtp--]); 18 } 19 int n,m; 20 int prm[11000000],prp=0,miu[11000000]; bool prg[11000000]; 21 int s[11000000]; 22 void gtmiu(){ 23 fill(prg+1,prg+n+1,0); 24 miu[1]=1; 25 for(int i=2;i<=n;++i){ 26 if(!prg[i]) prm[++prp]=i,miu[i]=-1; 27 for(int j=1;j<=prp && i*prm[j]<=n;++j){ 28 prg[i*prm[j]]=true; 29 if(!(i%prm[j])){ miu[i*prm[j]]=0; break;} 30 miu[i*prm[j]]=-miu[i]; 31 } 32 } 33 for(ll i=1;i<=n;++i) 34 s[i]=(s[i-1]+(i*i*miu[i])%mo)%mo; 35 } 36 ll sm(ll x,ll y){ return (((x*(x+1)/2)%mo)*((y*(y+1)/2)%mo))%mo;} 37 ll cclt(int x,int y){ 38 if(x>y) swap(x,y); 39 ll bwl=0; 40 for(ll i=1,j;i<=x;i=j+1){ 41 j=min(x/(x/i),y/(y/i)); 42 bwl=(bwl+((s[j]-s[i-1])*sm(x/i,y/i))%mo)%mo; 43 } 44 return bwl; 45 } 46 int main(){//freopen("ddd.in","r",stdin); 47 cin>>n>>m; 48 if(n>m) swap(n,m); 49 gtmiu(); 50 ll ans=0; 51 for(ll i=1,j;i<=n;i=j+1){ 52 j=min(n/(n/i),m/(m/i)); 53 ans=(ans+(((i+j)*(j-i+1)/2)%mo*cclt(n/i,m/i))%mo)%mo; 54 } 55 cout<<(ans+mo)%mo<<endl; 56 return 0; 57 }