luogu P4917 天守阁的地板
题目描述:
设 \(F(i,j)\) 表示用 \(i*j\) 大小的矩形 不旋转拼成一个边长任意的正方形所需要的最小块数
给出 \(T\) 组 \(n\),求每次询问中 \(\Pi_{i=1}^{n} \Pi_{j=1}^{n} F(i,j)\)
\(n \leq 10^6,T \leq 10^3\)
题解
一眼SB题不是吗
先考虑单个询问的情况,可以得到如下结论:
用\(a*b\)规格的地板摆成的正方形的边长最小是\(lcm(a,b)\)(木板不允许旋转),所以需要最小的木板数量是\(\frac{lcm(a,b)}{a}*\frac{lcm(a,b)}{b}\)
问题中说要把所有可能的\((a,b)\)的答案求乘积,那么问题就转换成了求$$\Pi_{i=1}{n}\Pi_{j=1}\frac{lcm(i,j)^2}{i*j}$$
首先你必须知道
先考虑分子:
所以可以先\(O(n)\)预处理出阶乘\(fac(i)\),用快速幂\(O(logn)\)算出分子
接下来考虑分母
为了方便,可以先求出$$\Pi_{i=1}{n}\Pi_{j=1}gcd(i,j)$$
再平方
考虑枚举\(d\),那么我们求出对于每个\(d\)有多少对\((i,j)\)满足\(gcd(i,j)=d\)再套用快速幂即可
显然,\(i,j\)都是\(d\)的倍数是必要条件,所以设\(i=k_1d,j=k_2d(k1,k2≤\lfloor \frac{n}{d} \rfloor)\)
那么当且仅当\(gcd(k1,k2)=1\)即\(k1,k2\)互质时符合\(gcd(i,j)=d\)的条件(否则\(gcd\)可以扩大为\(d*gcd(k1,k2)\))
不妨令 \(k1>k2\),那么符合条件的点对数 为每个在范围内的\(k1\),小于它且与它互质的数的个数的和(即\(\sumφ\)),所以真正的答案就是
对\(φ\)求出前缀和\(sum\)
所以$$\Pi_{i=1}{n}\Pi_{j=1}gcd(i,j)=\Pi_{d=1}{n}d \rfloor)*2-1}$$
好了,到现在复杂度为\(O(n+Tnlogn)\),可以拿到\(60\)分的好成绩(雾)
最后一个优化:
不难发现,\(\lfloor \frac{n}{d} \rfloor\)的取值只有\(2\sqrt{n}\)种,那么可以把\(\lfloor \frac{n}{d} \rfloor\)相等的所有\(d\)放在一起处理
假设某一段从\(i\)到\(j\)的所有数的这个值都等于\(x\),那么这一段的乘积就等于
那么先预处理出\(1-19260817\)的逆元\(inv(i)\),这一段的答案就是\((fac(j)*inv(fac(i-1)))^{sum(x)*2-1}\)
这样处理后的最终复杂度是\(O(n+T\sqrt{n}log(n))\),可以愉快的通过此题
\(AC code\)(主要部分)
#define LL long long
const int mod=19260817;
LL ans;
LL quickpow(LL a,LL k)
{
if(!k)return 1;
LL res=quickpow(a,k>>1);
res=(res*res)%mod;
if(k&1)res=(res*a)%mod;
return res;
}
void pre()
{
phi[1]=1;
for(LL i=2;i<maxn;++i)
{
if(!notprime[i])prime[++cnt]=i,phi[i]=i-1;
for(LL j=1;j<=cnt&&prime[j]*i<maxn;++j)
{
notprime[prime[j]*i]=1;
if(i%prime[j])phi[prime[j]*i]=phi[i]*(prime[j]-1);
else
{
phi[prime[j]*i]=phi[i]*prime[j];
break;
}
}
}
for(int i=1;i<maxn;++i)phi[i]=phi[i-1]+phi[i];
fac[0]=1;
for(int i=1;i<maxn;++i)fac[i]=((LL)fac[i-1]*i)%mod;
inv[0]=inv[1]=1;
for(int i=2;i<mod;++i)inv[i]=(((-(LL)(mod/i)*(LL)(inv[mod%i]))%mod)+mod)%mod;
}
int T;
int main()
{
pre();
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
ans=quickpow(fac[n],2*n);
LL ans2=1;
for(LL i=1,L;i<=n;i=L+1)
{
LL p=2*phi[n/i]-1;
L=n/(n/i);
ans2=(ans2*quickpow((fac[L]*(LL)inv[fac[i-1]])%mod,p))%mod;
}
ans2=(ans2*ans2)%mod;
printf("%lld\n",(ans*(LL)inv[ans2])%mod);
}
}