数三角形

\(O(nmlogn)\)

首先,我们处理本题,对于正向思维,即将所有的满足题意的三角形直接数出并不容易实现,我们可以考虑从反面入手,只要从所有的情况中减去不合法的情况即可

对于一条横向的线,不合法的数量为 \(C(m+1,3)\) ,同理,对于一条竖直的线,不合法的数量即为 \(C(n+1,3)\)

对于一条斜向的线,我们可以从两个点之间的距离入手,考虑这两个点之间不合法情况的数量,对于横向距离为 \(i\) , 对于纵向距离为 \(j\) 三角形,一共有 \((m-i+1) \times (n-j+1)\) 个,其中每个三角形对于不合法的数量的贡献为: \(gcd(i,j)-1\) ,同时,我们用这种方式只能处理出斜率非负的情况,再将其乘以 \(2\) 即可

复杂度: \(O(nmlogn)\)

code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<math.h>
#define ll long long

const ll maxn=1e3+10;
ll n,m;
ll vis[maxn][maxn];

inline ll cal(ll x)
{
	if(x<3) return 0;
	return x*(x-1)*(x-2)/6;
}

inline ll gcd(ll a,ll b)
{
	return b==0 ? a : gcd(b,a%b);
}

int main(void)
{
	scanf("%lld %lld",&n,&m);
	
	ll sum=cal((n+1)*(m+1));
	
	sum-=cal(n+1)*(m+1);
	sum-=cal(m+1)*(n+1);
	
	for(int i=1;i<=n;i++)
	{
	 	for(int j=1;j<=m;j++)
		{
			sum-=(n-i+1)*(m-j+1)*(gcd(i,j)-1)*2;
		}
	}
	
	printf("%lld\n",sum);
	
	return 0;
}

\(O(n)\)

我们可以考虑对于斜着的线的计数方案,即:

\[\displaystyle \sum_{i=1}^{n}{\displaystyle \sum_{j=1}^{m}{(n-i+1)(m-j+1)(gcd(i,j)-1)}} \]

那么,我们可以用 \(id\) 函数改写为:

\[\displaystyle \sum_{i=1}^{n}{\displaystyle \sum_{j=1}^{m}{(n-i+1)(m-j+1)[id(gcd(i,j)-1)]}} \]

卷开即得:

\[\displaystyle \sum_{i=1}^{n}{\displaystyle \sum_{j=1}^{m}{(n-i+1)(m-j+1) \displaystyle \sum_{d|gcd(i,j)}^{d \neq 1}{\varphi(d)}}} \]

变更枚举顺序得:

\[\displaystyle \sum_{d=2}^{min(n,m)}{\varphi(d) \displaystyle \sum_{i=1}^{\lfloor{\frac{n}{d}}\rfloor}{(n-i \times d +1 )} \displaystyle \sum_{j=1}^{\lfloor{\frac{m}{d}}\rfloor}{(m-j \times d +1 )}} \]

对于式 \(\displaystyle \sum_{i=1}^{\lfloor{\frac{n}{d}}\rfloor}{(n-i \times d +1 )}\) ,显然即为一个等差数列求和,其首项为 \(n-1\times d+1\) ,末项为 \(n- \lfloor{\frac{n}{d}}\rfloor \times d +1\) ,则和为

\[((n-1\times d+1)+(n- \lfloor{\frac{n}{d}}\rfloor \times d +1)) \times \lfloor{\frac{n}{d}}\rfloor \times \frac{1}{2} \]

即为:

\[{(n-d+(n \bmod d)+2)} \times \lfloor{\frac{n}{d}}\rfloor \times \frac{1}{2} \]

故总和为:

\[\frac{1}{4} \displaystyle \sum_{d=2}^{min(n,m)}{\varphi(d)}{{(n-d+(n \bmod d)+2)} \times \lfloor{\frac{n}{d}}\rfloor}{{(m-d+(m \bmod d)+2)} \times \lfloor{\frac{m}{d}}\rfloor} \]

则线性筛预处理欧拉函数后 \(O(n)\) 求和即可

code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<math.h>
#include<algorithm>
#define ll long long

const ll maxn=1e3+10;
ll n,m,tot,sum,ans;
ll phi[maxn],vis[maxn],prime[maxn];

inline ll cal(ll x)
{
	if(x<3) return 0;
	return x*(x-1)*(x-2)/6;
}

inline void pre(ll x)
{
	phi[1]=1;
	
	for(int i=2;i<=x;i++)
	{
		if(!vis[i])
		{
			vis[i]=1;
			prime[++tot]=i;
			phi[i]=i-1;
		}
		
		for(int j=1;j<=tot&&i*prime[j]<=x;j++)
		{
			vis[i*prime[j]]=1;
			
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			
			phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
}

int main(void)
{
	scanf("%lld %lld",&n,&m);
	
	pre(maxn-5);
	
	ans=cal((n+1)*(m+1));
	ans-=cal(n+1)*(m+1);
	ans-=cal(m+1)*(n+1); 	
	
//	for(int i=1;i<=100;i++)
//	{
//		printf("%lld\n",phi[i]);
//	}
	
	for(int i=2;i<=std::min(n,m);i++)
	{
		sum+=phi[i]*(n-i+(n%i)+2)*(n/i)*(m-i+(m%i)+2)*(m/i)/2;
	}
	
//	printf("%lld\n",sum);
	
	printf("%lld\n",ans-sum);
}
posted @ 2021-01-14 16:25  雾隐  阅读(140)  评论(0编辑  收藏  举报