题目:P4917 天守阁的地板
双倍经验:P5221 Product
先说这题怎么做。
首先我们知道可以铺的地板块数为lcm(a,b)*lcm(a,b)/(a*b)=a*b/gcd(a,b)*lcm(a,b)/(a*b)=lcm(a,b)/gcd(a,b)
然后就可以愉快的莫反了。博主惨兮兮不会用LaTeX写数学公式,所以。。搬了一张图。
原文章 因为这位大佬和我的做法很相似,所以借用了他的题解表达出我的想法。
于是我开开心心地做了一下,十分暴力地对于每个n求一遍,喜提60分的佳绩。。。
但我把这份代码改了一下交到P5221 Product,拿到了满分。
1 #include<stdio.h> 2 #define it register int 3 #define il inline 4 const int mod=104857601; 5 const int N=1000005; 6 typedef long long ll; 7 int n,mu[N],p[N/10+2],cnt; 8 bool isp[N]; 9 ll ans; 10 il void mui(){ 11 mu[1]=1; 12 register long long x; 13 for(it i=2;i<N;++i){ 14 if(!isp[i]) p[++cnt]=i,mu[i]=-1; 15 for(it j=1;(x=1ll*i*p[j])<N&&j<=cnt;++j){ 16 isp[x]=1; 17 if(!(i%p[j])) break; 18 mu[x]=-mu[i]; 19 } 20 } 21 for(it i=2;i<N;++i) mu[i]+=mu[i-1]; 22 } 23 il void ksm(ll x,it l,ll &now){ 24 now=1; 25 while(l){ 26 if(l&1) now=1ll*now*x%mod; 27 x=x*x%mod,l>>=1; 28 } 29 } 30 il void cal(it x,ll &now){ 31 now=0;for(it l=1,r;l<=x;l=r+1) r=x/(x/l),now=(now+1ll*(x/l)*(x/l)%(mod-1)*(mu[r]-mu[l-1]+mod-1)%(mod-1))%(mod-1); 32 } 33 int main(){ 34 scanf("%d",&n),mui();ans=1; 35 for(it i=2;i<=n;++i) ans=ans*i%mod; 36 ksm(ans,n<<1,ans); 37 ll now1=0,now2=0,s1=1,s2=1,now3=0; 38 it i=1,j=1; 39 for(it l=1,r;l<=n;l=r+1){ 40 r=n/(n/l); 41 while(i<=l-1) s1=s1*i%mod,++i; 42 while(j<=r) s2=s2*j%mod,++j; 43 ksm(s2,mod-2,now2),cal(n/l,now3),ksm(now2*s1%mod,2*now3,now2),ans=ans*now2%mod; 44 } 45 printf("%lld",ans); 46 return 0; 47 }
思考了一下,如果对于每个n求一遍,O(T*n)不超时就怪了。。
那么怎么办呢?最好是可以预处理,然后在sqrt(n)时间内完成
我们再看一眼分母,发现分母可以再化简化简:
我们可以枚举gcd,然后把式子化简成这样:
分子分母都有(n!),把分子里的提上去。于是就可以愉快滴敲代码啦。
1 #include<stdio.h> 2 #define it register int 3 #define il inline 4 const int N=1000005; 5 const int mod=19260817; 6 typedef long long ll; 7 int mu[N],p[N],T,cnt,n; 8 bool isp[N]; 9 ll ans,fac[N],o[N],ph[N]; 10 il void ksm(ll x,ll l,ll &now){ 11 now=1; 12 while(l){ 13 if(l&1) now=now*x%mod; 14 x=x*x%mod,l>>=1; 15 } 16 } 17 il void cal(it x,ll &now){ 18 now=0;for(it l=1,r;l<=x;l=r+1) r=x/(x/l),now=(now+1ll*(x/l)*(x/l)*(mu[r]-mu[l-1]+mod-1)%(mod-1))%(mod-1); 19 } 20 il void phi(){ 21 ph[1]=1; 22 register long long x; 23 for(it i=2;i<N;++i){ 24 if(!isp[i]) ph[i]=i-1,p[++cnt]=i; 25 for(it j=1;(x=1ll*i*p[j])<N&&j<=cnt;++j){ 26 isp[x]=1; 27 if(!(i%p[j])) {ph[x]=1ll*ph[i]*p[j];break;} 28 ph[x]=1ll*ph[i]*ph[p[j]]; 29 } 30 } 31 for(it i=2;i<N;++i) ph[i]=(ph[i]+ph[i-1])%(mod-1); 32 } 33 namespace io { 34 const int SIZE = (1 << 21) + 1; 35 char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; 36 int f, qr; 37 #define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++) 38 inline void flush () {fwrite (obuf, 1, oS - obuf, stdout);oS = obuf;} 39 template <class I> 40 inline void fr (I &x) { 41 for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1; 42 for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); 43 x *= f; 44 } 45 struct Flusher_ {~Flusher_() {flush();}} io_flusher_; 46 } 47 using io :: fr; 48 int main(){ 49 phi(); 50 fr(T); 51 fac[0]=fac[1]=1;for(it i=2;i<=1000000;++i) fac[i]=1ll*fac[i-1]*i%mod; 52 ll now1,now2; 53 while(T--){ 54 fr(n); 55 ans=1; 56 for(it l=1,r;l<=n;l=r+1){ 57 r=n/(n/l),ksm(fac[l-1],mod-2,now1),ksm(fac[r]*now1%mod,ph[n/l]%mod,now2),ans=ans*now2%mod; 58 } 59 ksm(fac[n],(n<<1|1)+1,now1),ksm(ans,mod-5,ans);//ans^4的逆元 60 printf("%lld ",ans*now1%mod); 61 } 62 return 0; 63 }