ACM-ICPC 2017 Asia Urumqi K.

2017新疆ICPC现场赛K.Sum of the Line

题意:


思路:先推推柿子





i^2的和不是积性函数,不能线性筛= =(强行分开筛感觉很麻烦)

这个柿子不会往下化简了。。但是化到这里已经可以做了

第一种思路:预处理mu,对每个n枚举d 暴力搞一波

这种思路不行,1e8的O(n)mu预处理都快T了QAQ

第二种思路:

考虑mu的定义,我们只需枚举所有对ans有贡献的n的因子d

那什么是有贡献的d呢?

这些d能写成不重复素数的乘积

比如d=30=2*3*5,mu(30)=-1,ok

像d=45=3*3*5就不是有贡献的d,mu(45)=0;

假设我们对n分解质因子之后得到p1=2,p2=7.....

选奇数个p形成的d,mu(d)=-1

选偶数个p形成的d,mu(d)=1

这就是为什么莫比乌斯函数mu也可以叫做容斥因子了。

素数预处理sqrt(1e8)以内的就可以了

2*3*5*7*11*13*17*19*23>1e8

即n最多只有9个质因子

容斥2^9种状态无压力~

复杂度O(10000+T*(512+logn))

难度0.85

c++247ms


#include<iostream>
#include<stdio.h>
#include<vector>
#define ll long long
using namespace std;
const int maxn=1e4+5;
const int mod=998244353;
ll pri[3005]; 
bool isp[maxn];
ll mu[maxn];
int p=0;
ll inv6;
vector<int>dd;
inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
ll qmod(ll a,ll b)
{
	ll res=1;
	while(b)
	{
		if(b&1)
			res=res*a%mod;
		a=a*a%mod;
		b=b>>1;
	}
	return res;
}
void get(ll n)
{
	isp[1]=1;
	mu[1]=1;
	for(ll i=2;i<=n;i++)
	{
		if(!isp[i])
		{
			pri[++p]=i;
			mu[i]=-1;
		}
		for(ll j=1;j<=p&&i*pri[j]<=n;j++)
		{
			isp[i*pri[j]]=1;
			if(i%pri[j]==0)//不互质
			{
				mu[i*pri[j]]=0;
			}
			else
			{
				mu[i*pri[j]]=-mu[i];
			}
		}
	}
}
void get2(int n)
{
	for(int i=1;i<=p&&n>1;i++)
	{
		if(n%pri[i]==0)
		{
			dd.push_back(pri[i]);
			n/=pri[i];
		}
		while(n%pri[i]==0)
		{
			n/=pri[i];
		}
	}
	if(n!=1)
		dd.push_back(n);
}
ll s(ll n)
{
	ll ans=(((n*(2*n+1)%mod)*(n+1))%mod*inv6)%mod;
	return ans;
}
int dfs(int n)
{
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		if(n%i==0)
		{
			ans+=i*i*mu[i]*s(n/i);
		}
	}
	return ans;
}
int main()
{
	//cout<<2*3*5*7*11*13*17*21*23<<endl;
	inv6=qmod(6,mod-2);
	get(10000);
//	cout<<p<<endl;
//	for(int i=1;i<=100;i++)
//	{
//		printf("%d\n",dfs(i));
//	}
	int T;
	cin>>T;
	while(T--)
	{
		ll n;
		scanf("%lld",&n);
		dd.clear();
		get2(n);
//		for(int i=0;i<dd.size();i++)
//			printf("p:%d\n",dd[i]);
		ll ans=0;
		for(int i=0;i<(1<<dd.size());i++)
		{
			int t=1;int cnt=0;
			for(int j=0;j<dd.size();j++)
			{
				if(i&(1<<j))
				{
					t*=dd[j];
					cnt++;
				}
			}
		//	printf("test:%d\n",t);
			if(cnt%2==0)
				ans+=1ll*t*t*s(n/t),ans%=mod;
			else
				ans-=1ll*t*t*s(n/t),ans=(ans+mod)%mod;
		}
		printf("%lld\n",(ans+mod)%mod);
	//	puts("");
	}
	
} 







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值