BZOJ 4815 CQOI2017 小Q的表格 欧拉函数+分块
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4815
题意概述:要认真概述的话这个题就出来了。。。
分析:
首先分析题目,认真研究一下修改操作,想到一个问题:满足什么样的条件的格子会互相影响?
看到式子,一想,这正是辗转相除?迅速意识到行列的gcd相同的格子会互相影响。
然后我们再利用一下系数的关系,把式子变成f(a,a+b)/(a+b)=f(a,b)/b,发现当行相同的时候格子之间的值与所处列数成正比关系,因为题目保证了f(a,b)=f(b,a),同样的性质也会出现在列相同的情况下。对于任意两个互相影响的格子,都可以先变换行坐标再变换列坐标互相到达,于是有:f(a,b)/(a*b)=f(x,y)/(x*y)。
然后你发现有了这个性质,我们就可以用一维空间来储存所有的格子的值了~
令gcd(i,j)=g,那么f(i,j)=f(g,g)*(i*j)/(g*g)。我们令f(g)=f(g,g),所有格子(i,j)的值都可以由f(gcd(i,j))得到。
所以有:
我们令:,则
考虑一下s(x)的计算,我们枚举i,然后枚举所有小于i的j,如果i,j互质,我们就统计进入答案,可以看成是统计所有小于i且与i互质的j的和乘以i统计进入答案,根据对称性,另一半方阵是一样的,单独处理对角线上的唯一有贡献的(1,1)即可。
有一个神奇的公式:小于i且和i互质的正整数的和为phi(i)*i/2(证明:对于一个小于i的x,gcd(i,x)=1,有gcd(i,i-x)=1,也就是说小于i且与i互质的数是成对出现的,并且每一对的和为i,一共有phi(i)/2对)。(对s(x)的计算也可以莫比乌斯反演,借助[gcd(i,j)==1]=[sum{ mu(k) | k|i,k|j,即k|gcd(i,j) }==1],但是我只探索出了70分的世界线......)
所以:,可以直接预处理出来。
最后我们只要用一个分块支持O(sqrt(n))修改,O(1)查询,加上用O(sqrt(k))枚举k的约数就可以做到每组询问O(sqrt(n)+sqrt(k))的时间回答了(有的时候分块的O(1)询问是个很棒的东西,弄个树状数组就自寻烦恼了,分块有点小细节,一定要把f(x)的值原原本本地存起来,不能取模)。
时间复杂度O(M(sqrt(N)+sqrt(K)))。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<set> 9 #include<map> 10 #include<vector> 11 #include<cctype> 12 using namespace std; 13 const int MAXN=4000005; 14 const int mo=1000000007; 15 typedef long long LL; 16 17 int M,N,K; 18 int pri[MAXN],tot,phi[MAXN],s[MAXN]; 19 bool ntp[MAXN]; 20 struct Block{ 21 static const int maxn=4000005; 22 static const int size=2999; 23 static const int maxm=2010; 24 int sum[maxn],flag[maxm],L[maxm],R[maxm],cnt,sz,belong[maxn]; 25 LL f[maxn]; 26 Block(){ sz=0; } 27 void build(int n){ 28 for(sz=1;sz+size<n;sz+=size){ 29 cnt++,L[cnt]=sz,R[cnt]=sz+size; 30 for(int j=L[cnt];j<R[cnt];j++) 31 f[j]=1ll*j*j,sum[j]=(sum[j-1]+f[j]%mo)%mo,belong[j]=cnt; 32 flag[cnt]=0; 33 } 34 cnt++,L[cnt]=sz,R[cnt]=n+1; 35 for(int j=L[cnt];j<R[cnt];j++) 36 f[j]=1ll*j*j,sum[j]=(sum[j-1]+f[j]%mo)%mo,belong[j]=cnt; 37 flag[cnt]=0; 38 sz=n; 39 } 40 void update(int p,LL v){ 41 int delt=((v-f[p])%mo+mo)%mo; 42 f[p]=v; 43 for(int i=p;i<R[belong[p]];i++) 44 sum[i]=(sum[i]+delt)%mo; 45 for(int i=belong[p]+1;i<=cnt;i++) 46 flag[i]=(flag[i]+delt)%mo; 47 } 48 int query(int p){ 49 return (sum[p]+flag[belong[p]])%mo; 50 } 51 }block; 52 53 void ready() 54 { 55 ntp[0]=ntp[1]=1,phi[1]=1; 56 for(int i=2;i<=N;i++){ 57 if(!ntp[i]) pri[++tot]=i,phi[i]=i-1; 58 for(int j=1;j<=tot&&1ll*pri[j]*i<=N;j++){ 59 ntp[pri[j]*i]=1; 60 if(i%pri[j]==0){ 61 phi[i*pri[j]]=phi[i]*pri[j]; 62 break; 63 } 64 phi[i*pri[j]]=phi[i]*(pri[j]-1); 65 } 66 } 67 block.build(N); 68 for(int i=1;i<=N;i++) 69 s[i]=(s[i-1]+1ll*i*i%mo*phi[i]%mo)%mo; 70 } 71 int gcd(int x,int y){ return !y?x:gcd(y,x%y); } 72 void work() 73 { 74 scanf("%d%d",&M,&N); 75 ready(); 76 int a,b,k,p,ans,d; LL x; 77 for(int i=1;i<=M;i++){ 78 scanf("%d%d%lld%d",&a,&b,&x,&k); 79 p=gcd(a,b); 80 block.update(p,x/(a/p)/(b/p)); 81 ans=0; 82 for(int g=1,last;g<=k;g=last+1){ 83 last=k/(k/g); 84 d=((block.query(last)-block.query(g-1))%mo+mo)%mo; 85 ans=(ans+1ll*s[k/g]*d%mo)%mo; 86 } 87 printf("%d\n",ans); 88 } 89 } 90 int main() 91 { 92 work(); 93 return 0; 94 }