传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4818
思路:
看到这道题我就想到当年noip模拟赛T1 是一个伪装的”很好”的辗转相除而我做了半天的悲惨经历。。感动的落泪。
首先注意到那个规则就是辗转相除,不过不是标准形式,化成标准形式后多迭代几次很容易发现性质:
f(a,b)=a∗bgcd(a,b)2∗f(gcd(a,b),gcd(a,b))
这个通过归纳法也很容易证明
这样ans = ∑ki=1∑kj=1f(i,j)
化简这个式子,注意到一个经典问题:
∑ni=1[(i,n)=1]i=n∗(φ+e)2
利用这个我们的式子变成了:
∑ni=1f(i,i)∗g(⌊ni⌋)
其中 g(n)=∑ni=1i∗i∗ϕ(i)
可以做到线性预处理 g
那么暴力维护修改的话就是套一个bit,明显T飞了。。。
到这里就非常像我bzoj 3529的做法了,一道披着数论外衣的数据结构狼?
注意到修改和查询是不均衡的,用序列分块就可以解决了,
这样就能做到 O(1) 查询, O(n√)修改 ,总的复杂度: O(m∗n√)
另外按bzoj 3529那个题的做法我们也可以用定期重构的分块解决这个问题,这不过这里因为 n 很大的原因,重构次数不能太多,我估计在5次左右就可以了。。。应该也是可以通过的吧。
感觉自己基础差的要死。。。一开始连 φ 都筛错了半天没看出来,难怪滚粗呢QAQ
代码(分块):
#include
#include
#include
#include
#include
#define N 4000002
using namespace std;
typedef long long LL;
LL n,m,phi[N],g[N],now[N];
const LL P = 1000000007LL;
inline void in(LL &x)
{
char c;
while (!isdigit(c = getchar()));
x = (c ^ 48);
while (isdigit(c = getchar())) x = 10 * x + (c ^ 48);
}
inline LL gcd(LL a,LL b)
{
return (!b) ? (a) : gcd(b,a % b);
}
inline void inc(LL &x,LL y)
{
x += y;
if (x >= P) x -= P;
}
int cnt,prime[N];
bool not_prime[N];
inline void Sieve()
{
memset(not_prime,0,sizeof(not_prime));
cnt = 0; phi[1] = 1;
for (int i = 2;i <= n; ++i)
{
if (!not_prime[i])
{
prime[++cnt] = i;
phi[i] = i - 1;
}
for (int j = 1;j <= cnt; ++j)
if (prime[j] * i > n) break;
else
{
not_prime[prime[j] * i] = 1;
if (i % prime[j])
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
else
{
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
}
}
g[0] = 0;
for (int i = 1;i <= n; ++i)
g[i] = (g[i - 1] + 1LL * i * i % P * phi[i] % P) % P;
}
LL S[N];
int num,belong[N],lb[N],rb[N],B;
inline void Divide()
{
B = (int)(sqrt(n)) ;
for (int i = 1;i <= n; ++i)
belong[i] = (i - 1) / B + 1;
belong[0] = belong[n + 1] = -1;
for (int i = 1;i <= n; ++i)
{
if (belong[i] != belong[i - 1]) lb[belong[i]] = i;
if (belong[i] != belong[i + 1]) rb[belong[i]] = i;
}
num = belong[n];
for (int i = 1;i <= num; ++i)
{
int le = lb[i],re = rb[i];
S[le] = now[le] % P;
for (int j = le + 1;j <= re; ++j)
S[j] = (S[j - 1] + now[j]) % P;
}
}
inline void Pre()
{
for (int i = 1;i <= n; ++i)
now[i] = 1LL * i * i;
Sieve();
Divide();
}
inline void init()
{
in(m); in(n);
Pre();
}
inline void change(LL a,LL b,LL x,LL k)
{
LL last,GCD = gcd(a,b),delta;
last = now[GCD];
now[GCD] = x / (a * b / (GCD * GCD));
delta = (now[GCD] - last) % P;
if (delta < 0) delta += P;
int re = rb[belong[GCD]];
for (int i = GCD;i <= re; ++i)
inc(S[i],delta);
}
LL ans;
inline LL GET(LL l,LL r)
{
int ll = belong[l],rr = belong[r];
LL Sum = 0;
if (ll == rr)
{
if (l == lb[ll]) inc(Sum,S[r]);
else inc(Sum,(S[r] - S[l - 1] + P) % P);
return Sum;
}
Sum = S[r];
if (l == lb[ll]) inc(Sum,S[rb[ll]]);
else inc(Sum,(S[rb[ll]] - S[l - 1] + P) % P);
for (int i = ll + 1;i < rr; ++i)
inc(Sum,S[rb[i]]);
return Sum;
}
inline void Query(LL a,LL b,LL x,LL k)
{
ans = 0;
for (int i = 1,j;i <= k;i = j + 1)
{
j = k / (k / i);
inc(ans,g[k / i] * GET(i,j) % P);
}
printf("%lld\n",ans);
}
LL a,b,x,k;
inline void DO_IT()
{
for (int i = 1;i <= m; ++i)
{
in(a); in(b); in(x); in(k);
change(a,b,x,k);
Query(a,b,x,k);
}
}
int main()
{
init();
DO_IT();
return 0;
}