4815: [Cqoi2017]小Q的表格

题意:

对于n*n的数表,任意一个位置,需要满足:
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×f(a,a+b)=(a+b)*f(a,b) b×f(a,a+b)=(a+b)f(a,b)
每次修改一个位置,需要将相关位置全部修改且求前k行前k列的和。

题解:

将二式变化可得:
f ( a , a + b ) a ( a + b ) = f ( a , b ) a b \frac{f(a,a+b)}{a(a+b)}=\frac{f(a,b)}{ab} a(a+b)f(a,a+b)=abf(a,b)
所以
f ( a , b ) a b = f ( a , a − b ) a ( a − b ) = f ( a , a   m o d b ) a ( a   m o d b ) = f ( d , d ) d 2 \frac{f(a,b)}{ab}=\frac{f(a,a-b)}{a(a-b)}=\frac{f(a,a\ modb)}{a(a\ modb)}=\frac{f(d,d)}{d^2} abf(a,b)=a(ab)f(a,ab)=a(a modb)f(a,a modb)=d2f(d,d)
其中 d = g c d ( a , b ) d=gcd(a,b) d=gcd(a,b)
所以所有 g c d ( i , j ) = g c d ( a , b ) gcd(i,j)=gcd(a,b) gcd(i,j)=gcd(a,b)的位置都会受影响。
枚举 d d d
a n s = ∑ d n f ( d ) ∑ i ⌊ n d ⌋ ∑ j ⌊ n d ⌋ [ g c d ( i , j ) = = 1 ] i j ans=\sum_d^nf(d)\sum_i^{\lfloor \frac n d \rfloor}\sum_j^{\lfloor \frac n d \rfloor}[gcd(i,j)==1]ij ans=dnf(d)idnjdn[gcd(i,j)==1]ij
s ( n ) = ∑ i n ∑ j n [ g c d ( i , j ) = = 1 ] i j s(n)=\sum_i^n\sum_j^{n}[gcd(i,j)==1]ij s(n)=injn[gcd(i,j)==1]ij
根据 ∑ i n [ ( i , n ) = = 1 ] i = ϕ ( i ) 2 ∗ n \sum_i^n[(i,n)==1]i=\frac{\phi(i)}{2}*n in[(i,n)==1]i=2ϕ(i)n
可得:
s ( n ) = ∑ i n i 2 ϕ ( i ) s(n)=\sum_i^n i^2\phi(i) s(n)=ini2ϕ(i)
a n s = ∑ d n f ( d ) s ( ⌊ n d ⌋ ) ans=\sum_d^nf(d)s(\lfloor \frac n d \rfloor) ans=dnf(d)s(dn)
分块即可。
但还有一个问题, f f f是待修改的,假如树状数组求和过不去。
所以用普通分块。 O ( m l o g n ) O(mlogn) O(mlogn)
code:

#include
#include
#include
#include
#include
#define LL long long
using namespace std;
const LL mod=1e9+7;
int m,n,L[2010],R[2010],cur[4000010];
int phi[4000010],pr=0,prime[4000010],h[4000010],f[4000010],sum[4000010];
int s[4000010],S[2010];
bool v[4000010];
void pre()
{
	memset(v,true,sizeof(v));
	phi[1]=1;
	for(int i=2;i<=4000000;i++)
	{
		if(v[i]) prime[++pr]=i,phi[i]=i-1;
		for(int j=1;j<=pr&&prime[j]*i<=4000000;j++)
		{
			int s=prime[j]*i;v[s]=false;
			if(i%prime[j]==0) {phi[s]=phi[i]*prime[j];break;}
			phi[s]=phi[i]*(prime[j]-1);
		}
	}
	for(int i=1;i<=4000000;i++) h[i]=(h[i-1]+(LL)i*i%mod*phi[i]%mod)%mod;
	for(int i=1;i<=4000000;i++) f[i]=(LL)i*i%mod,sum[i]=(sum[i-1]+f[i])%mod;
}
void change(int k,int c)
{
	for(int i=k;i<=R[cur[k]];i++) (s[i]+=c)%=mod;
	for(int i=cur[k];i<=cur[n];i++) (S[i]+=c)%=mod;
}
int gcd(int a,int b) {return a==0?b:gcd(b%a,a);}
int get(int k) {return (s[k]+S[cur[k]-1])%mod;}
int main()
{
	pre();
	scanf("%d %d",&m,&n);
	int len=sqrt(n);
	for(int i=1;i<=n;i++)
	{
		cur[i]=(i/len)+1;
		if(cur[i]!=cur[i-1]) L[cur[i]]=i,R[cur[i-1]]=i-1;
	}
	R[cur[n]]=n;
	for(int i=1;i<=n;i++)
	{
		s[i]=s[i-1];
		if(L[cur[i]]==i) s[i]=0,S[cur[i]]=S[cur[i]-1];
		s[i]=(s[i]+f[i])%mod;(S[cur[i]]+=f[i])%=mod;
	}
	while(m--)
	{
		int a,b,k;LL x;scanf("%d %d %lld %d",&a,&b,&x,&k);
		int d=gcd(a,b);a/=d;b/=d;
		x=x/a/b;x%=mod;change(d,-f[d]);f[d]=(int)x;change(d,f[d]);
		int i=1,j=0,ans=0;
		while(i<=k)
		{
			j=k/(k/i);
			ans=(ans+(LL)h[k/i]*(get(j)-get(i-1))%mod)%mod;
			i=j+1;
		}
		printf("%d\n",(ans+mod)%mod);
	}
}

你可能感兴趣的:(莫比乌斯反演,分块)