zoukankan      html  css  js  c++  java
  • 【CF870F】Paths 分类讨论+数学

    【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。求$sumlimits_{1le 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;
    }
  • 相关阅读:
    Leetcode#104. Maximum Depth of Binary Tree(二叉树的最大深度)
    Leetcode#70. Climbing Stairs(爬楼梯)
    Leetcode#88. Merge Sorted Array(合并两个有序数组)
    美团2019秋招后台开发编程题题解
    Leetcode#191. Number of 1 Bits(位1的个数)
    Leetcode#461. Hamming Distance(汉明距离)
    Leetcode#13. Roman to Integer(罗马数字转整数)
    Leetcode#521. Longest Uncommon Subsequence I(最长特殊序列 Ⅰ)
    Leetcode#557. Reverse Words in a String III(反转字符串中的单词 III)
    带你剖析WebGis的世界奥秘----瓦片式加载地图
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/8157601.html
Copyright © 2011-2022 走看看