【CQOI2017】小Q的表格(数论,分块)

题意:

有一个无限大的整数表格 f f f 满足以下两条法则:

  • f ( a , b ) = f ( b , a ) f(a,b)=f(b,a) f(a,b)=f(b,a)
  • b × f ( a , a + b ) = ( a + b ) × f ( a , b ) b\times f(a,a+b)=(a+b)\times f(a,b) b×f(a,a+b)=(a+b)×f(a,b)

初始时 f ( a , b ) = a × b f(a,b)=a\times b f(a,b)=a×b。有 m m m 次修改,每次修改会改变某个位置,并将所有此次修改会影响到的所有位置按照法则重置一遍。每次修改完还会询问你前 k k k 行前 k k k 列里所有数的和对 1 0 9 + 7 10^9+7 109+7 取模的结果( k k k 不固定)。

m ≤ 1 0 4 m\leq 10^4 m104,修改位置行数、列数和 k k k 均不超过 4 × 1 0 6 4\times 10^6 4×106

题解:

找性质的方向和感觉还是不太对,推了半天看题解才反应过来那两条法则实际上在讲什么……

我们先将第二条法则移项得到 f ( a , a + b ) a + b = f ( a , b ) b → f ( a , b ) b = f ( a , b − a ) b − a \frac{f(a,a+b)}{a+b}=\frac{f(a,b)}{b}\to \frac{f(a,b)}{b}=\frac{f(a,b-a)}{b-a} a+bf(a,a+b)=bf(a,b)bf(a,b)=baf(a,ba),同时再结合第一条法则得到 f ( a , b ) a = f ( a − b , a ) a − b \frac{f(a,b)}{a}=\frac{f(a-b,a)}{a-b} af(a,b)=abf(ab,a)。二者结合一下,得到 f ( a , b ) a b = f ( a , b − a ) a ( b − a ) = f ( a − b , a ) ( a − b ) a \frac{f(a,b)}{ab}=\frac{f(a,b-a)}{a(b-a)}=\frac{f(a-b,a)}{(a-b)a} abf(a,b)=a(ba)f(a,ba)=(ab)af(ab,a),类似一个辗转相除的过程,最后得到:
f ( a , b ) a b = f ( d , d ) d 2 f ( d ⋅ a , d ⋅ b ) = a b ⋅ f ( d , d ) [ gcd ⁡ ( a , b ) = 1 ] \begin{aligned} \frac{f(a,b)}{ab}&=\frac{f(d,d)}{d^2}\\ f(d\cdot a,d\cdot b)&=ab\cdot f(d,d)&[\gcd(a,b)=1] \end{aligned} abf(a,b)f(da,db)=d2f(d,d)=abf(d,d)[gcd(a,b)=1]
也就是说,本质上是所有的 f ( d , d ) f(d,d) f(d,d) 确定了所有的 f ( a , b ) f(a,b) f(a,b),而一次修改相当于修改了一次 f ( d , d ) f(d,d) f(d,d)

答案即为:
∑ d = 1 k f ( d , d ) ∑ a = 1 ⌊ k / d ⌋ ∑ b = 1 ⌊ k / d ⌋ a b [ ( a , b ) = 1 ] \begin{aligned} &\sum_{d=1}^kf(d,d)\sum_{a=1}^{\lfloor k/d\rfloor}\sum_{b=1}^{\lfloor k/d\rfloor}ab[(a,b)=1]\\ \end{aligned} d=1kf(d,d)a=1k/db=1k/dab[(a,b)=1]
考虑先求出 g ( n ) = ∑ a = 1 n ∑ b = 1 n a b [ ( a , b ) = 1 ] g(n)=\sum_{a=1}^{n}\sum_{b=1}^nab[(a,b)=1] g(n)=a=1nb=1nab[(a,b)=1],这是与 f f f 无关的,可以提前预处理出来:
g ( n ) = g ( n − 1 ) − [ n = 1 ] + 2 ∑ i = 1 n n i [ ( n , i ) = 1 ] = g ( n − 1 ) − [ n = 1 ] + 2 n ∑ i = 1 n i ∑ j ∣ n , j ∣ i μ ( j ) = g ( n − 1 ) − [ n = 1 ] + 2 n ∑ j ∣ n μ ( j ) ∑ i = 1 ⌊ n / j ⌋ i j = g ( n − 1 ) − [ n = 1 ] + 2 n ∑ j ∣ n μ ( j ) ⋅ j ⋅ S ( ⌊ n / j ⌋ ) \begin{aligned} g(n)&=g(n-1)-[n=1]+2\sum_{i=1}^n ni[(n,i)=1]\\ &=g(n-1)-[n=1]+2n\sum_{i=1}^n i\sum_{j|n,j|i}\mu(j)\\ &=g(n-1)-[n=1]+2n\sum_{j|n}\mu(j)\sum_{i=1}^{\lfloor n/j\rfloor}ij\\ &=g(n-1)-[n=1]+2n\sum_{j|n}\mu(j)\cdot j \cdot S(\lfloor n/j\rfloor) \end{aligned} g(n)=g(n1)[n=1]+2i=1nni[(n,i)=1]=g(n1)[n=1]+2ni=1nijn,jiμ(j)=g(n1)[n=1]+2njnμ(j)i=1n/jij=g(n1)[n=1]+2njnμ(j)jS(n/j)
注意到使 μ ( j ) ≠ 0 \mu(j)\neq 0 μ(j)=0 j j j 的取值只有 2 ω ( n ) 2^{\omega(n)} 2ω(n) 种(其中 ω ( n ) \omega(n) ω(n) 表示 n n n 的不同质因子个数),而由于 2 ω ( n ) ≤ d ( n ) 2^{\omega(n)}\leq d(n) 2ω(n)d(n),所以 ∑ i = 1 n 2 ω ( i ) ≤ ∑ i = 1 n d ( n ) = O ( n log ⁡ n ) \sum_{i=1}^n 2^{\omega(i)}\leq \sum_{i=1}^nd(n)=O(n\log n) i=1n2ω(i)i=1nd(n)=O(nlogn)。进一步地,发现当 n = 1 0 6 n=10^6 n=106 ∑ i = 1 n 2 ω ( i ) \sum_{i=1}^n2^{\omega(i)} i=1n2ω(i) 约为 8 × 1 0 6 8\times 10^6 8×106,所以递推时只要暴力枚举每个 j j j 即可。


看完题解后发现我们还能进一步简化 g ( n ) g(n) g(n)
g ( n ) = g ( n − 1 ) − [ n = 1 ] + 2 n ∑ j ∣ n μ ( j ) ⋅ j ⋅ S ( ⌊ n / j ⌋ ) = g ( n − 1 ) − [ n = 1 ] + 2 n ∑ j ∣ n μ ( j ) ⋅ j ⋅ S ( n / j ) = g ( n − 1 ) − [ n = 1 ] + n ∑ j ∣ n μ ( j ) ⋅ j ⋅ n j ⋅ ( n j + 1 ) = g ( n − 1 ) − [ n = 1 ] + n 2 ∑ j ∣ n μ ( j ) ( n j + 1 ) = g ( n − 1 ) + n 2 ∑ j ∣ n μ ( j ) n j = g ( n − 1 ) + n 2 φ ( n ) \begin{aligned} g(n)&=g(n-1)-[n=1]+2n\sum_{j|n}\mu(j)\cdot j \cdot S(\lfloor n/j\rfloor)\\ &=g(n-1)-[n=1]+2n\sum_{j|n}\mu(j)\cdot j\cdot S(n/j)\\ &=g(n-1)-[n=1]+n\sum_{j|n}\mu(j)\cdot j\cdot \frac{n}{j}\cdot \left(\frac{n}{j}+1\right)\\ &=g(n-1)-[n=1]+n^2\sum_{j|n}\mu(j)\left(\frac{n}{j}+1\right)\\ &=g(n-1)+n^2\sum_{j|n}\mu(j)\frac{n}{j}\\ &=g(n-1)+n^2\varphi(n) \end{aligned} g(n)=g(n1)[n=1]+2njnμ(j)jS(n/j)=g(n1)[n=1]+2njnμ(j)jS(n/j)=g(n1)[n=1]+njnμ(j)jjn(jn+1)=g(n1)[n=1]+n2jnμ(j)(jn+1)=g(n1)+n2jnμ(j)jn=g(n1)+n2φ(n)
于是现在的递推时严格 O ( k ) O(k) O(k) 的了。


于是我们求出了所有的 g ( n ) g(n) g(n),现在考虑怎么求答案。

如果我们能做到快速求 f ( d , d ) f(d,d) f(d,d) 的前缀和的话,单次询问可以整除分块做到 O ( k ) O(\sqrt k) O(k ),足以通过。

现在的问题是处理 f f f,我们需要 O ( m ) O(m) O(m) 次单点修改 f f f O ( m k ) O(m\sqrt k) O(mk ) 次询问 f f f 的前缀和。

为了均衡时间复杂度,我们考虑分块,这样可以做到 O ( k ) O(\sqrt k) O(k ) 单点修改, O ( 1 ) O(1) O(1) 前缀查询。总时间复杂度 O ( k + m k ) O(k+m\sqrt k) O(k+mk )

#include<bits/stdc++.h>

#define SN 2010
#define N 4000010
#define ll long long

using namespace std;

namespace modular
{
	const int mod=1000000007;
	inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
	inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
	inline int mul(int x,int y){return 1ll*x*y%mod;}
	inline void Add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
	inline void Dec(int &x,int y){x=x-y<0?x-y+mod:x-y;}
	inline void Mul(int &x,int y){x=1ll*x*y%mod;}
}using namespace modular;

inline int poww(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1) ans=mul(ans,a);
		a=mul(a,a);
		b>>=1;
	}
	return ans;
}

inline ll read()
{
	ll x=0;
	int f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int m,n,f[N];
int len,B,bl[N],sumf[N],tag[SN];
int cnt,prime[N],phi[N],g[N];
bool notprime[N];

void init()
{
	phi[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!notprime[i])
		{
			prime[++cnt]=i;
			phi[i]=i-1;
		}
		for(int j=1,v;j<=cnt&&(v=i*prime[j])<=n;j++)
		{
			notprime[v]=1;
			if(!(i%prime[j]))
			{
				phi[v]=phi[i]*prime[j];
				break;
			}
			phi[v]=phi[i]*phi[prime[j]];
		}
	}
	for(int i=1;i<=n;i++)
		g[i]=add(g[i-1],mul(mul(i,i),phi[i]));
	len=sqrt(n);
	for(int i=1;i<=n;i++)
	{
		bl[i]=(i-1)/len+1;
		sumf[i]=add(sumf[i-1],f[i]=mul(i,i));
	}
	B=bl[n];
}

int gcd(int a,int b)
{
	return b?gcd(b,a%b):a;
}

void update(int x,int y)
{
	int b=bl[x];
	for(int i=x,r=min(n,b*len);i<=r;i++) Add(sumf[i],y);
	for(int i=b+1;i<=B;i++) Add(tag[i],y);
}

int query(int l,int r)
{
	return dec(add(sumf[r],tag[bl[r]]),add(sumf[l-1],tag[bl[l-1]]));
}

int main()
{
	m=read(),n=read();
	init();
	while(m--)
	{
		int a=read(),b=read(),k;
		ll y=read();k=read();
		int d=gcd(a,b);
		int fd=(y/(a/d)/(b/d))%mod;
		update(d,dec(fd,f[d]));
		f[d]=fd;
		int ans=0;
		for(int l=1,r;l<=k;l=r+1)
		{
			r=k/(k/l);
			Add(ans,mul(query(l,r),g[k/l]));
		}
		printf("%d\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值