【BZOJ4815】[CQOI2017]小Q的表格(莫比乌斯反演,分块)

【BZOJ4815】[CQOI2017]小Q的表格(莫比乌斯反演,分块)

题面

BZOJ
洛谷

题解

神仙题啊。
首先\(f(a,b)=f(b,a)\)告诉我们矩阵只要算一半就好了。
接下来是\(b*f(a,a+b)=(a+b)*f(a,b)\)
这个式子怎么看呢?

\[\begin{aligned}b*f(a,a+b)&=(a+b)*f(a,b)\\\frac{f(a,a+b)}{a+b}&=\frac{f(a,b)}{b}\\\frac{f(a,a+b)}{a*(a+b)}&=\frac{f(a,b)}{a*b}\end{aligned} \]

这个式子很明显类似于辗转相减的式子,如果我们令\(c=(a+b)\),那么等式就可以写成

\[\frac{f(a,c)}{a*c}=\frac{f(a,c-a)}{a*(c-a)} \]

那么进一步,意味着我们可以写成辗转相除的式子。

\[\frac{f(a,b)}{a*b}=\frac{f(a,a\%b)}{a*(a\%b)} \]

所以,类似于求\(gcd\)的终止状态,我们可以利用这个写出一个等式:

\[\frac{f(a,b)}{a*b}=\frac{f(gcd(a,b),gcd(a,b)}{gcd^2(a,b)} \]

为了方便后面直接令\(d=gcd(a,b)\),即等式为

\[f(a,b)=\frac{a*b}{d^2}f(d,d) \]

那么,不难发现,单次的修改只会对于\(d\)相等的位置产生影响。
考虑\(ans\)是个啥玩意

\[\begin{aligned}ans&=\sum_{i=1}^k\sum_{j=1}^kf(i,j)\\&=\sum_{d=1}^kf(d,d)\sum_{i=1}^k\sum_{j=1}^k\frac{i*j}{d^2}[gcd(i,j)=d]\\&=\sum_{d=1}^kf(d,d)\sum_{i=1}^{k/d}\sum_{j=1}^{k/d}ij[gcd(i,j)=1]\end{aligned} \]

后面一半的式子可以用莫比乌斯反演直接计算值。
当然了,也可以这样子推:

\[\begin{aligned}\sum_{i=1}^n\sum_{j=1}^nij[gcd(i,j)=1]&=2*\sum_{i=1}^ni\sum_{j=1}^ij[gcd(i,j)=1]\\&=2*\sum_{i=1}^n\frac{i\varphi(i)}{2}\\&=\sum_{i=1}^ni^2\varphi(i)\end{aligned} \]

后面那一步化简简单证明一下,假设\(x\)\(n\)互质,那么\(n-x\)也与\(n\)互质,因此所有与\(n\)互质的数的和是两两配对的。
也就是后面这一部分是可以提前预处理出来的,因为是\(k/d\),也就是等价于可以数论分块,所以我们现在唯一要做的就是维护\(f(i,i)\)的前缀和。
而修改操作显然是把当前位置会影响的位置的值全部对应的扩倍。也就是会影响到\(f(gcd,gcd)\)这个位置的值。我们要找个方法快速计算\(f(i,i)\)的前缀和。
考虑数据范围,操作次数很少,但是操作的范围很大。单次询问的时候我们有一个\(\sqrt k\)的数论分块的复杂度,也就是\(2*10^3\),操作次数有\(10^4\),所以我们显然不能带\(log\)的计算前缀和。那么考虑分块,将计算前缀和的复杂度将至\(O(1)\),而修改复杂度变为\(O(\sqrt n)\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MOD 1000000007
#define MAX 4000400
#define BLK 2200
inline ll read()
{
	ll x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int fpow(int a,int b)
{
	int s=1;
	while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
	return s;
}
int phi[MAX],pri[MAX],tt,s[MAX];
bool zs[MAX];
void Pre(int n)
{
	phi[1]=1;
	for(int i=2;i<=n;++i)
	{
		if(!zs[i])pri[++tt]=i,phi[i]=i-1;
		for(int j=1;j<=tt&&i*pri[j]<=n;++j)
		{
			zs[i*pri[j]]=true;
			if(i%pri[j])phi[i*pri[j]]=phi[i]*(pri[j]-1);
			else{phi[i*pri[j]]=phi[i]*pri[j];break;}
		}
	}
	for(int i=1;i<=n;++i)s[i]=1ll*i*i%MOD*phi[i]%MOD;
	for(int i=1;i<=n;++i)s[i]=(s[i]+s[i-1])%MOD;
}
int pre[BLK],ps[BLK],bs[BLK][BLK],val[MAX];
int b[MAX],tot,blk;
int n,m;
int Query(int x){if(!x)return 0;return (pre[b[x]-1]+bs[b[x]][x-blk*(b[x]-1)])%MOD;}
void Modify(int x,int w)
{
	val[x]=w;int p=x-blk*(b[x]-1);
	for(int i=x;b[i]==b[x];++i,++p)
		bs[b[x]][p]=(bs[b[x]][p-1]+val[i])%MOD;
	ps[b[x]]=bs[b[x]][p-1];
	for(int i=1;i<=tot;++i)pre[i]=(pre[i-1]+ps[i])%MOD;
}
int Calc(int k)
{
	int ret=0;
	for(int i=1,j;i<=k;i=j+1)
	{
		j=k/(k/i);
		ret=(ret+1ll*(Query(j)-Query(i-1)+MOD)*s[k/i])%MOD;
	}
	return ret;
}
int main()
{
	m=read();n=read();Pre(n);blk=sqrt(n);
	for(int i=1;i<=n;++i)b[i]=(i-1)/blk+1;
	tot=b[n];
	for(int i=1;i<=n;++i)val[i]=1ll*i*i%MOD;
	for(int i=1;i<=tot;++i)
		for(int j=1,a=(i-1)*blk+1;j<=blk&&a<=n;++j,++a)
			ps[i]=(ps[i]+val[a])%MOD,bs[i][j]=(bs[i][j-1]+val[a])%MOD;
	for(int i=1;i<=tot;++i)pre[i]=(pre[i-1]+ps[i])%MOD;
	while(m--)
	{
		int a=read(),b=read(),x=read()%MOD,k=read();
		int d=__gcd(a,b);
		Modify(d,1ll*x*d%MOD*d%MOD*fpow(1ll*a*b%MOD,MOD-2)%MOD);
		printf("%d\n",Calc(k));
	}
	return 0;
}
posted @ 2018-12-05 14:59  小蒟蒻yyb  阅读(396)  评论(0编辑  收藏  举报