题解:数论+容斥原理
注意这个题的意思是在平面内不断的发生镜面反射,让你寻找从左下角到右上角的路径。我们可以倒着每次找出对应出入射点的虚像,然后连线,不断把得到的直线关于镜面的边缘对称过去,最终一定会得到一条过左下角的直线。
那么问题其实可以转换成有一个d*d的网格,我们要在网格中选择一个点(x,y),要满足x+y-2=d,其实(0,0)到(x,y)的连线经过的格子的边界就是每次入射的位置,那么我们要求在路径中不能经过任何角落,所以我们需要保证这条直线不经过任何的整点,也就是gcd(x,y)=1.
那么答案就是下面式子的二分之一,因为每个点都计算了两次,因为(x^2+y^2)*(a^2+b^2)得到的实际上是个四项式,也就是(x,y)与(y,x)的答案。
那么这个问题解决的关键就是如何求1到n内与n互质的数的平方和(发现上面的式子是之所以算了两次,是因为每个合法的i^2都算了两次,其实我们可以直接计算1到n内与n互质的数的平方和,这个一定是两两对应形成点对)。
我们设n=d+2,那么其实可以用容斥原理来解决这个问题,我们要求的数是满足gcd(x,n)=1的数,也就是与n相同的质因子的个数是0的时候。ans=至少有0个与n相同的质因子-至少有1个与n相同的质因子+至少有2个与n相同的质因子....
至少有0个的其实就是1到n的平方和,有公式n(n+1)(2*n+1)/6
至少有1个,那么x必然是p(质因子)的倍数,也就是sigma(i=1..(n/p)) i*p ,这些数的平方和其实就是p^2*sigma(i=1..n/p)i^2
然后写dfs求解就可以了。
#include
#include
#include
#include
#include
#define N 100000
#define LL long long
#define p 1000000007
using namespace std;
LL prime[N+10],pd[N+10],num[N],cnt;
LL a,b,d,ans,n;
LL pow(LL x)
{
return x*x%p;
}
LL mul(LL num,LL x)
{
LL ans=1; LL base=num%p;
while (x){
if (x&1) ans=ans*base%p;
x>>=1;
base=base*base%p;
}
return ans;
}
LL sqrsum(LL x)
{
LL t=x*(x+1)%p*(2*x+1)%p*mul(6,p-2)%p;
//cout<N) break;
pd[prime[j]*i]=1;
if (i%prime[j]==0) break;
}
}
}
void solve(LL d)
{
for (int i=1;prime[i]*prime[i]<=d;i++)
if (d%prime[i]==0){
num[++cnt]=prime[i];
while (d%prime[i]==0) d/=prime[i];
}
if (d>1) num[++cnt]=d;
}
LL getf(LL i)
{
LL size=n/i;
LL ans=sqrsum(size)*pow(i);
return ans%p;
}
void dfs(LL tot,int x,int now)
{
if (now>cnt){
//if (x==0) return;
if (x%2) ans-=getf(tot);
else ans+=getf(tot);
ans=(ans%p+p)%p;
//cout<