[CQOI2017] 小Q的表格(分块 + 整除分块 + 数学 + 前缀和)
problem
luogu-P3700
solution
-
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)
會發現修改 f(a,b)f(a,b)f(a,b) 就影響 f(a,a+b)f(a,a+b)f(a,a+b) 進而影響 f(a,a+2b)…f(a,a+2b)\dotsf(a,a+2b)…
對于這個信息,如果對數學很敏感的選手,就會直接猜跟 gcd?\gcdgcd 掛鉤了。
向我們這種凡人呢就只能聽天由命了
考慮這個性質之所以很難使用上,是因為這兩個式子的系數只有一項,且是與等式對面的 f(x,y)f(x,y)f(x,y) 掛鉤的。
這種跟對面掛鉤的我們會想到比例式,因為我們清楚如果分母和分子相同是可以約分的。
當寫成比例式 f(a,a+b)a+b=f(a,b)b\frac{f(a,a+b)}{a+b}=\frac{f(a,b)}{b}a+bf(a,a+b)?=bf(a,b)?,我們才會稍微察覺到如果兩邊都再除以一個 aaa,整體形式就很同構了。
f(a,a+b)a?(a+b)=f(a,b)a?b\frac{f(a,a+b)}{a*(a+b)}=\frac{f(a,b)}{a*b}a?(a+b)f(a,a+b)?=a?bf(a,b)? 加減運算不影響 gcd?\gcdgcd 的值。
遞歸輾轉相除最后就會得到 f(a,b)a?b=f(gcd?(a,b),gcd?(a,b))gcd?(a,b)2\frac{f(a,b)}{a*b}=\frac{f\big(\gcd(a,b),\gcd(a,b)\big)}{\gcd(a,b)^2}a?bf(a,b)?=gcd(a,b)2f(gcd(a,b),gcd(a,b))?。
所以我們可以得出:修改 f(x,y)f(x,y)f(x,y) 只會影響 gcd?(x,y)=gcd?(a,b)\gcd(x,y)=\gcd(a,b)gcd(x,y)=gcd(a,b) 的所有 f(a,b)f(a,b)f(a,b) 的值。
繼續考慮處理詢問,這就要推式子了。
∑i=1n∑j=1nf(i,j)=∑i=1n∑j=1ni×jgcd?(i,j)2f(i,j)=∑d=1nf(d,d)∑i=1?nd?∑j=1?nd?i×j?[gcd?(i,j)=1]\sum_{i=1}^n\sum_{j=1}^nf(i,j)=\sum_{i=1}^n\sum_{j=1}^n\frac{i\times j}{\gcd(i,j)^2}f(i,j)=\sum_{d=1}^nf(d,d)\sum_{i=1}^{\lfloor\frac nd\rfloor}\sum_{j=1}^{\lfloor\frac nd\rfloor}i\times j·[\gcd(i,j)=1] i=1∑n?j=1∑n?f(i,j)=i=1∑n?j=1∑n?gcd(i,j)2i×j?f(i,j)=d=1∑n?f(d,d)i=1∑?dn???j=1∑?dn???i×j?[gcd(i,j)=1]
∑k=1n[gcd?(k,n)=1]?k=∑k=1nk∑d∣kμ(d)=∑d∣nμ(d)∑d∣kk=∑d∣nμ(d)?d∑i=1?nd?i=∑d∣nμ(d)?d?(?nd?+1)??nd?2=n?(∑d∣nμ(d)+n∑d∣nμ(d)d)2=n?(φ(n)+[n=1])2\sum_{k=1}^n[\gcd(k,n)=1]·k\\ =\sum_{k=1}^nk\sum_{d\mid k}\mu(d)=\sum_{d\mid n}\mu(d)\sum_{d\mid k}k=\sum_{d\mid n}\mu(d)*d\sum_{i=1}^{\lfloor\frac nd\rfloor}i\\ =\sum_{d\mid n}\mu(d)*d*\frac{(\lfloor\frac nd\rfloor+1)*\lfloor\frac nd\rfloor}{2}=\frac{n·\Big(\sum_{d\mid n}\mu(d)+n\sum_{d\mid n}\frac{\mu(d)}ze8trgl8bvbq\Big)}{2}\\ =\frac{n·\Big(\varphi(n)+[n=1]\Big)}{2} k=1∑n?[gcd(k,n)=1]?k=k=1∑n?kd∣k∑?μ(d)=d∣n∑?μ(d)d∣k∑?k=d∣n∑?μ(d)?di=1∑?dn???i=d∣n∑?μ(d)?d?2(?dn??+1)??dn???=2n?(∑d∣n?μ(d)+n∑d∣n?dμ(d)?)?=2n?(φ(n)+[n=1])?
根據兩個基礎數論結論:∑d∣nμ(d)=[n=1],∑d∣nμ(d)d=φ(n)n\sum_{d\mid n}\mu(d)=[n=1],\sum_{d\mid n}\frac{\mu(d)}ze8trgl8bvbq=\frac{\varphi(n)}{n}∑d∣n?μ(d)=[n=1],∑d∣n?dμ(d)?=nφ(n)? 得出最后一個等式。
∑d=1nf(d,d)∑i=1?nd?∑j=1?nd?i×j?[gcd?(i,j)=1]≠∑d=1nf(d,d)∑i=1?nd?∑j=1?nd?i?i?(φ(i)+[i=1])2\sum_{d=1}^nf(d,d)\sum_{i=1}^{\lfloor\frac nd\rfloor}\sum_{j=1}^{\lfloor\frac nd\rfloor}i\times j·[\gcd(i,j)=1]\ne\sum_{d=1}^nf(d,d)\sum_{i=1}^{\lfloor\frac nd\rfloor}\sum_{j=1}^{\lfloor\frac nd\rfloor}i·\frac{i·(\varphi(i)+[i=1])}{2} d=1∑n?f(d,d)i=1∑?dn???j=1∑?dn???i×j?[gcd(i,j)=1]?=d=1∑n?f(d,d)i=1∑?dn???j=1∑?dn???i?2i?(φ(i)+[i=1])?
這個式子之所以不能直接用上面的結論,是因為這里的 jjj 可能大于 iii,根據對稱性,×2\times 2×2 即可。
但這樣就會把所有 i=ji=ji=j 的情況重復算了一遍,但除了 i=j=1i=j=1i=j=1 外其余都不滿足最大公約數為 111 的限制。
i=j=1i=j=1i=j=1 的貢獻是 111,而 1×φ(1)+[i=1]2×2=2\frac{1\times \varphi(1)+[i=1]}{2}\times 2=221×φ(1)+[i=1]?×2=2,我們可以直接扔掉 [i=1][i=1][i=1],這樣這種情況算出來的貢獻就是對的。
且對于 i>1i>1i>1 的情況去掉 [i=1][i=1][i=1] 的特判沒有影響。
=∑d=1nf(d,d)∑i=1?nd?i?i?φ(i)2×2=∑d=1nf(d,d)∑i=1?nd?i2φ(i)=\sum_{d=1}^nf(d,d)\sum_{i=1}^{\lfloor\frac nd\rfloor}i·\frac{i*\varphi(i)}{2}\times2=\sum_{d=1}^nf(d,d)\sum_{i=1}^{\lfloor\frac nd\rfloor}i^2\varphi(i) =d=1∑n?f(d,d)i=1∑?dn???i?2i?φ(i)?×2=d=1∑n?f(d,d)i=1∑?dn???i2φ(i)
記 S(n)=∑i=1ni2φ(i)S(n)=\sum_{i=1}^ni^2\varphi(i)S(n)=∑i=1n?i2φ(i),則答案即為 ∑d=1nf(d,d)S(?nd?)\sum_{d=1}^nf(d,d)S(\lfloor\frac nd\rfloor)∑d=1n?f(d,d)S(?dn??)。
然后似乎就可以整除分塊了,然而整除分塊還要用到 fff 的前綴和。
很遺憾,這個 fff 還要是支持修改的,所以需要一個數據結構。
樹狀數組查詢 O(mnlog?n)O(m\sqrt n\log n)O(mn?logn) 不太行,但修改卻只有一個 log?\loglog 很快。當然這也能過。
我們需要平衡一下查詢和修改的時間復雜度,那就是分塊啦!
直接把 fff 變成 fff 的前綴和,單點修改變成區間修改 gcd?(a,b)~n\gcd(a,b)\sim ngcd(a,b)~n,之后查詢前綴和就變成了單點詢問了。
具體而言 f(a,b)←xf(a,b)\leftarrow xf(a,b)←x,即為 f(gcd?(a,b),gcd?(a,b))←x?gcd?(a,b)2abf(\gcd(a,b),\gcd(a,b))\leftarrow \frac{x·\gcd(a,b)^2}{ab}f(gcd(a,b),gcd(a,b))←abx?gcd(a,b)2?(這個是由上面最開始遞歸輾轉相除的式子變形得來的)
code
#include <bits/stdc++.h> using namespace std; #define int long long #define mod 1000000007 #define maxn 4000005 #define maxB 2005 int m, n, cnt; int a[maxn], block[maxn], L[maxB], R[maxB], f[maxn], g[maxB], phi[maxn], s[maxn], prime[maxn], vis[maxn];int gcd( int x, int y ) {if( ! y ) return x;else return gcd( y, x % y ); }void init() {int B = sqrt( n );for( int i = 1;i <= n;i ++ ) {a[i] = i * i % mod;f[i] = ( f[i - 1] + a[i] ) % mod;block[i] = ( i - 1 ) / B + 1;}for( int i = n;i >= 1;i -- ) L[block[i]] = i; for( int i = 1;i <= n;i ++ ) R[block[i]] = i; } void modify( int l, int r, int x ) {int k = x - a[l]; a[l] = x;if( block[l] == block[r] )for( int i = l;i <= r;i ++ ) f[i] = ( f[i] + k ) % mod;else {for( int i = l;i <= R[block[l]];i ++ ) f[i] = ( f[i] + k ) % mod;for( int i = L[block[r]];i <= r;i ++ ) f[i] = ( f[i] + k ) % mod;for( int i = block[l] + 1;i < block[r];i ++ ) g[i] = ( g[i] + k ) % mod;} } int query( int x ) { return ( f[x] + g[block[x]] ) % mod; }void sieve() {phi[1] = 1;for( int i = 2;i <= n;i ++ ) {if( ! vis[i] ) prime[++ cnt] = i, phi[i] = i - 1;for( int j = 1;j <= cnt and i * prime[j] <= n;j ++ ) {vis[i * prime[j]] = 1;if( i % prime[j] == 0 ) {phi[i * prime[j]] = phi[i] * prime[j] % mod;break;}elsephi[i * prime[j]] = phi[i] * phi[prime[j]] % mod;}}for( int i = 1;i <= n;i ++ ) s[i] = ( s[i - 1] + i * i % mod * phi[i] ) % mod; } int solve( int n ) {int ans = 0;for( int l = 1, r;l <= n;l = r + 1 ) {r = n / ( n / l );ans = ( ans + ( query( r ) - query( l - 1 ) ) * s[n / l] ) % mod;}return ans; }signed main() {scanf( "%lld %lld", &m, &n );init();sieve();for( int i = 1, a, b, x, k;i <= m;i ++ ) {scanf( "%lld %lld %lld %lld", &a, &b, &x, &k );int d = gcd( a, b );x = x / ( a / d ) / ( b / d ) % mod;modify( d, n, x );printf( "%lld\n", ( solve( k ) + mod ) % mod );}return 0; }總結
以上是生活随笔為你收集整理的[CQOI2017] 小Q的表格(分块 + 整除分块 + 数学 + 前缀和)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 新浪微博悄然上线微博聊天网页版 和微信P
- 下一篇: [TJOI2011] 书架(线段数优化d