【2020CCPC网络赛】1002 Graph Theory Class(HDU6889)

题目链接

题意:给你一个n个结点的完全图,结点从1~n标号,结点i和j之间的边权为lcm(i+1,j+1),问你这个图的最小生成树的边权和是多少

思路:为了方便起见,我们将节点从2~n+1编号,可以知道,当新加入的节点是个质数时,最小生成树的权值之和增加的是两倍的质数值,因为把这个点和2连起来是加的值最小的,当新加入节点的值不是质数时,权值之和增加的是这个数的值,因为前面一定会存在一个数是这个数的因子,只要把这个数和它的因子连起来,增加的值就只是这个数本身的值了。

我们可以把所有质数提一个出来,则增加的数是以3为首项,公差为1的等差数列。用等差数列求和公式,O(1)的时间求出来这个等差数列前n项和。再用min25筛,O( n**(3/4) / logn)的时间求出质数的前n项和,两项相加即可得出结果。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef __int128 lll;
const int maxn=1e5+5;
int prime[maxn];
ll ps[maxn];
bool vis[maxn];
int cnt=1;
const int N = 1000010;
typedef long long LL;
namespace Min25
{
	int prime[N], id1[N], id2[N], flag[N], ncnt, m;
	LL g[N], sum[N], a[N], T, n;
	inline void fff()
	{
		for(int i=0; i<=N; i++)
		{
			prime[i]=0;
			id1[i]=0;
			id2[i]=0;
			flag[i]=0;
			g[i]=0;
			sum[i]=0;
			a[i]=0;
		}
		ncnt=0;
		m=0;
		T=0;
		n=0;
	}
	inline int ID(LL x)
	{
		return x <= T ? id1[x] : id2[n / x];
	}
	inline LL calc(LL x)
	{
		return x * (x + 1) / 2 - 1;
	}
	inline LL f(LL x)
	{
		return x;
	}
	inline void init()
	{
		T = sqrt(n + 0.5);
		for (int i = 2; i <= T; i++)
		{
			if (!flag[i]) prime[++ncnt] = i, sum[ncnt] = sum[ncnt - 1] + i;
			for (int j = 1; j <= ncnt && i * prime[j] <= T; j++)
			{
				flag[i * prime[j]] = 1;
				if (i % prime[j] == 0) break;
			}
		}
		for (LL l = 1; l <= n; l = n / (n / l) + 1)
		{
			a[++m] = n / l;
			if (a[m] <= T) id1[a[m]] = m;
			else id2[n / a[m]] = m;
			g[m] = calc(a[m]);
		}
		for (int i = 1; i <= ncnt; i++)
			for (int j = 1; j <= m && (LL)prime[i] * prime[i] <= a[j]; j++)
				g[j] = g[j] - (LL)prime[i] * (g[ID(a[j] / prime[i])] - sum[i - 1]);
	}
	inline LL solve(LL x)
	{
		if (x <= 1) return x;
		return n = x, init(), g[ID(n)];
	}
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)//50
	{
		ll n,k;
		scanf("%lld%lld",&n,&k);
		Min25::fff();
		__int128 ans=(lll)(n+4)*(n-1)/2;//printf("%lld\n", (ll)ans);
		ans += Min25::solve(n+1) -2;
		ans %= k;
		printf("%lld\n", (ll)ans);
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值