1. 程式人生 > >【CF870F】Paths 分類討論+數學

【CF870F】Paths 分類討論+數學

ons min n-2 type != con 數學 質數 cstring

【CF870F】Paths

題意:一張n個點的圖,對於點i,j(i!=j),如果gcd(i,j)!=1,則i到j有一條長度為1的無向邊。令dis(i,j)表示從i到j的最短路,如果i無法到j,則dis(i,j)=0。求$\sum\limits{1\le i < j \le n}dis(i,j)$。

n<=10^7

題解:容易發現dis(i,j)不超過3,所以我們可以分出好多種情況討論一下,但是每種情況都不好搞啊。

我們先把點1扔了,算出總點對數。我們定義一個數x是壞的當且僅當x是質數且x>n/2。然後討論:

1.dis(x,y)=0。這種情況發生當且僅當x或y是壞的,容易計算答案。

2.dis(x,y)=1。就是求有多少不互質的數對嘛,用歐拉函數算一下就行。
3.dis(x,y)=2。我們設x的最小質因子為p(x),那麽這樣的路徑形如x->p(x)p(y)->y。此時還要討論:
  1.如果x,y都是質數,則xy<=n,這個暴力統計就行。
  2.如果x是好質數y是合數,則x*p(y)<=n且x不是y的約數。我們先求出所有x*p(y)<=n的個數,然後去掉x是y的約數的點對。
    這個怎麽算呢?如果x==p(y),這樣的點對數很容易求。如果x>p(y),我們可以從大到小枚舉x,那麽y/x<=n/x,我們同時枚舉所有的y/x,如果p(y/x)小於x,那麽我們統計上它的貢獻;否則它對以後的x都不會產生貢獻。最後我們再把p(y/x)=x的去掉即可。

  3.如果x,y是互質的合數,依舊用歐拉函數算一下就行。
4.dis(x,y)=3。形如x->2p(x)->2p(y)->y。用總數-上面的3個即可得到。

#include <cstdio>
#include <cstring>
#include <iostream>
const int N=10000010;
typedef long long ll;
int pri[N/5],mn[N],sx[N],phi[N],sp[N],sn[N];
int n,num,m;
ll cnt0,cnt1,cnt2,cnt3,tot,now;
inline int min(const int &a,const int &b) {return a<b?a:b;}
int main()
{
	scanf("%d",&n);
	int i,j;
	phi[1]=mn[1]=1;
	for(i=2;i<=n;i++)
	{
		if(!sx[i])
		{
			pri[++num]=i,mn[i]=i,phi[i]=i-1,sx[i]=1;
			if(i<=n/2)	m=num;
		}
		sp[i]=num;
		for(j=1;j<=num&&i*pri[j]<=N;j++)
		{
			mn[i*pri[j]]=pri[j];
			if(i%pri[j]==0)
			{
				sx[i*pri[j]]=sx[i],phi[i*pri[j]]=phi[i]*pri[j];
				break;
			}
			sx[i*pri[j]]=sx[i]+1,phi[i*pri[j]]=phi[i]*(pri[j]-1);
		}
	}
	tot=1ll*(n-1)*(n-2)/2;
	for(i=m+1;i<=num;i++)	cnt0+=pri[i]-2+n-pri[i]-(num-i);
	for(i=2;i<=n;i++)	cnt1+=i-1-phi[i];
	for(i=2;i<=n;i++)	if(mn[i]!=i)	cnt2+=phi[i]-sp[i]+sx[i]-1;
	for(i=2;i<=n;i++)	if(mn[i]!=i)
	{
		cnt2+=min(m,sp[n/mn[i]]);
		if(1ll*mn[i]*mn[i]<=n)	cnt2--;
	}
	for(j=2,i=m;i>=1;i--)
	{
		for(;j<=n/pri[i];j++)	if(mn[j]<pri[i])	sn[mn[j]]++,now++;
		now-=sn[pri[i]];
		cnt2-=now;
	}
	for(i=1;i<=m;i++)	for(j=1;j<i&&pri[i]*pri[j]<=n;j++)	cnt2++;
	cnt3=tot-cnt0-cnt1-cnt2;
	printf("%lld",cnt1+cnt2*2+cnt3*3);
	return 0;
}

【CF870F】Paths 分類討論+數學