[BZOJ4407]于神之怒加强版

题面戳我
Description
给下N,M,K.求

\[\sum_{i=1}^{n}\sum_{j=1}^{m}\gcd(i,j)^k\quad(mod\quad1e9+7) \]

Input
输入有多组数据,输入数据的第一行两个正整数T,K,代表有T组数据,K的意义如上所示,下面第二行到第T+1行,每行为两个正整数N,M,其意义如上式所示。
Output
如题
Sample Input
1 2
3 3
Sample Output
20
HINT
1<=N,M,K<=5000000,1<=T<=2000

sol

首先单组数据\(O(n)\)的都会噻,就不讲了。(就是内外数论分块\(O(\sqrt n*\sqrt n)=O(n)\)的那种)
这种方法化到最后的答案式应该是

\[ans=\sum_{d=1}^{n}d^k\sum_{i=1}^{n/d}\mu(i)\lfloor\frac n{di}\rfloor\lfloor\frac m{di}\rfloor \]

我们现在令\(T=di\),然后考虑分别计算每一个\(\lfloor\frac nT\rfloor\lfloor\frac mT\rfloor\)对答案的贡献

\[ans=\sum_{T=1}^{n}\lfloor\frac nT\rfloor\lfloor\frac mT\rfloor*\sum_{d|T}d^k\mu(\frac Td) \]

(手玩一下发现就是这样的)
前面那一坨显然还是\(O(\sqrt n)\)分块,后面那一坨发现是一个狄利克雷卷积的形式,就是说

\[h(T)=\sum_{d|T}d^k\mu(\frac Td) \]

是一个积性函数。所以线性筛出来后维护一个前缀和即可。

code

这份代码比yyb的慢了整整一倍。
原因是我已经被int溢出给搞怕了所以全开long long(是全开!)
所以说如果你想像yyb 这种港记跑得一样快的话请使用int类型并在适当的地方加上1ll*
我不会说我因为ans没有清零WA了两次的

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
const ll N = 5000005;
const ll maxn = 5000000;
const ll mod = 1e9 + 7;
ll gi()
{
	ll x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
ll fastpow(ll a,ll b)
{
	ll s=1;
	while (b)
	{
		if (b&1) s=s*a%mod;
		a=a*a%mod;b>>=1;
	}
	return s;
}
ll t,k,n,m,pri[N],tot,zhi[N],low[N],h[N],ans;
void Mobius()
{
	zhi[1]=h[1]=low[1]=1;
	for (ll i=2;i<=maxn;i++)
	{
		if (!zhi[i]) low[i]=pri[++tot]=i,h[i]=(fastpow(i,k)-1+mod)%mod;
		for (ll j=1;j<=tot&&i*pri[j]<=maxn;j++)
		{
			zhi[i*pri[j]]=1;
			if (i%pri[j]==0)
			{
				low[i*pri[j]]=low[i]*pri[j];
				if (low[i]==i)
					h[i*pri[j]]=h[i]*(h[pri[j]]+1)%mod;
				else
					h[i*pri[j]]=h[i/low[i]]*h[low[i]*pri[j]]%mod;
				break;
			}
			low[i*pri[j]]=pri[j];
			h[i*pri[j]]=h[i]*h[pri[j]]%mod;
		}
	}
	for (ll i=1;i<=maxn;i++) h[i]=(h[i]+h[i-1])%mod;
}
int main()
{
	t=gi();k=gi();
	Mobius();
	while (t--)
	{
		n=gi();m=gi();ans=0;
		if (n>m) swap(n,m);
		ll i=1;
		while (i<=n)
		{
			ll j=min(n/(n/i),m/(m/i));
			ans=(ans+(h[j]-h[i-1]+mod)%mod*((n/i)*(m/i)%mod)%mod)%mod;
			i=j+1;
		}
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2018-01-10 22:35  租酥雨  阅读(503)  评论(0编辑  收藏  举报