把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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

点此看题面

大致题意: 给定一个\(n\times n\)的表格,初始状态下\((a,b)\)格子的值为\(a\times b\)。每次修改一个位置上的值,并要求你通过修改其他位置上的值使得表格满足\(f(a,b)=f(b,a)\)\(b\times f(a,a+b)=(a+b)\times f(a,b)\)。每次修改后询问前\(k\)\(k\)列内数字的和。

前言

我还是太弱了,式子推到一半就推不下去,最后还是只能去跪膜闪指导的博客。

这题还真是挺神奇的啊。

修改

首先,经过简单的推导可以发现,修改了\(f(a,b)\)之后,设\(b\equiv m(mod\ a),a\equiv n(mod\ b)\),则首先受到影响的就是\(f(a,m),f(m,a),f(b,n),f(n,b)\)

然后进一步搞下去,就会发现这是个类似于辗转相减法的东西,设\(gcd(a,b)=gcd(x,y)\),则最终受影响的应该就是所有的\(f(x,y)\)

\(gcd(a,b)=d\),而我们有\(x\times y\times f(d,d)=d^2\times f(x,y)\)(显然可以从原式推得),因此:

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

\[f(x,y)=\frac{f(d,d)\times xy}{d^2} \]

也就是说,对于一次修改,我们可以快速计算出\(f(d,d)\)修改后的值,而其他所有的值都可以从\(f(d,d)\)推导得出。

于是,原本很复杂的题面,立刻简单了许多。

询问

又是一波大力推式子:

\[\sum_{i=1}^k\sum_{j=1}^kf(i,j) \]

显然,我们可以枚举\(gcd\),然后就能把从上面得到的\(f(x,y)=\frac{f(d,d)\times xy}{d^2}\)这个式子代入,得到:

\[\sum_{d=1}^kf(d,d)\sum_{i=1}^{\lfloor\frac kd\rfloor}\sum_{i=1}^{\lfloor\frac kd\rfloor}ij[gcd(i,j)=1] \]

按照莫比乌斯反演的一般套路,有\(\sum_{p|i,p|j}\mu(p)=[gcd(i,j)=1]\),然后先枚举\(p\),得到:

\[\sum_{d=1}^kf(d,d)\sum_{p=1}^{\lfloor\frac kd\rfloor}\mu(p)\sum_{i=1}^{\lfloor\frac k{dp}\rfloor}\sum_{j=1}^{\lfloor\frac k{dp}\rfloor}(ip)\cdot(jp) \]

从后式中提出\(p\),并设\(S(n)=\sum_{i=1}^ni\),于是就有:

\[\sum_{d=1}^kf(d,d)\sum_{p=1}^{\lfloor\frac kd\rfloor}\mu(p)p^2S(\lfloor\frac k{dp}\rfloor)^2 \]

这看起来似乎不能再进一步化简了,但复杂度依旧堪忧。

事实上,还有一种很神奇的差分套路。

显然有\(\lfloor\frac n{i}\rfloor-\lfloor\frac {n-1}i\rfloor=[i|n]\)

因此,如果我们设\(F(x)=\sum_{p=1}^x\mu(p)p^2S(\lfloor\frac xp\rfloor)^2\),并用\(F(x)\)减去\(F(x-1)\)(差分),就能得到:

\[\sum_{p|n}\mu(p)p^2(S(\frac xp)^2-S(\frac xp-1)^2) \]

单独拿出其中的\(S(\frac xp)^2-S(\frac xp-1)^2\)来分析,对于\((\sum_{i=1}^ai)^2-(\sum_{i=1}^{a-1}i)^2\),运用平方差公式+高斯求和得到:

\[(2\sum_{i=1}^{a}i-a)\cdot a=(a(a+1)-a)\cdot a=a^3 \]

因此,原式就等同于:

\[\sum_{p|n}\mu(p)p^2(\frac xp)^3=x^2\sum_{p|n}\mu(p)\frac xp=x^2\sum_{p|n}\mu(p)\times Id(\frac xp) \]

然后我们就发现了,式子中的\(\sum_{p|n}\mu(p)\times Id(\frac xp)\)显然是一个狄利克雷卷积啊!

而我们知道\(\mu*Id=\phi\),因此就得到了很简单的一个式子:

\[x^2\cdot\phi(x) \]

那么只要一遍线性筛筛出\(\phi\),就能方便地计算出\(F(x)\)了。

最终,我们只要通过分块实现对\(f(d,d)\)\(O(\sqrt n)\)修改、\(O(1)\)求值,并用除法分块计算答案即可。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 4000000
#define X 1000000007
#define Qinv(x) Qpow(x,X-2)
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
#define XSum(x,y) ((x)+(y)>=X?(x)+(y)-X:(x)+(y))
#define XSub(x,y) ((x)<(y)?(x)-(y)+X:(x)-(y))
using namespace std;
int n;
class LinearSieve//线性筛
{
	private:
		int Pt,P[N+5],phi[N+5];
	public:
		int f[N+5];
		I void Sieve()
		{
			RI i,j,t;for(phi[1]=1,i=2;i<=n;++i)//筛phi
				for(!P[i]&&(phi[P[++Pt]=i]=i-1),j=1;j<=Pt&&(t=i*P[j])<=n;++j)
					if(P[t]=1,i%P[j]) phi[t]=phi[i]*(P[j]-1);else {phi[t]=phi[i]*P[j];break;}
			for(i=1;i<=n;++i) f[i]=(1LL*i*i%X*phi[i]+f[i-1])%X;//累加求出f
		}
}S;
class BlockSolver//分块
{
	private:
		int sz,a[N+5],bl[N+5],V[N+5],BV[N+5];
		I void BF(CI l,CI r,CI v) {for(RI i=l;i<=r;++i) Inc(V[i],v);}//散块暴力
	public:
		I void Init()
		{
			sz=sqrt(n);for(RI i=1;i<=n;++i)//预处理,赋初始值
				bl[i]=(i-1)/sz+1,a[i]=1LL*i*i%X,V[i]=XSum(V[i-1],a[i]);
		}
		I void U(CI p,CI nv)//修改
		{
			RI v=XSub(nv,a[p]);a[p]=nv;//计算变化值
			if(bl[p]==bl[n]) return BF(p,n,v);BF(p,bl[p]*sz,v),BF((bl[n]-1)*sz+1,n,v);
			for(RI i=bl[p]+1;i^bl[n];++i) Inc(BV[i],v);//整块标记
		}
		I int Q(CI x) {return XSum(V[x],BV[bl[x]]);}//询问
}B;
I int gcd(CI x,CI y) {return y?gcd(y,x%y):x;}
I int Qpow(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
int main()
{
	RI Qt,a,b,k,t;long long x;scanf("%d%d",&Qt,&n),S.Sieve(),B.Init();W(Qt--)
	{
		scanf("%d%d%lld%d",&a,&b,&x,&k),t=gcd(a,b),B.U(t,(x%X)*t%X*t%X*Qinv(1LL*a*b%X)%X);//计算f(d,d)
		for(t=0,a=1;a<=k;a=b+1) b=k/(k/a),t=(1LL*(B.Q(b)-B.Q(a-1)+X)*S.f[k/a]+t)%X;printf("%d\n",t);//除法分块求答案
	}return 0;
}
posted @ 2020-05-08 11:14  TheLostWeak  阅读(113)  评论(0编辑  收藏  举报