CQOI 2017 小Q的表格 - 不一樣的暴力

  題目太長了略去不表。
  聽說這個題正解是O(n+mn) 的,然而我太菜只會暴力。下面來講講我的搞笑做法。


  觀察f(a,b) 的等式,可以寫成f(a,b)=b/(ab)f(a,ba),b>a ,進一步的是f(a,b)=b(a%b)f(a,a%b) ,若設g=gcd(a,b) ,則f(a,b)=k(a,b)f(g,g) ,其中k(a,b) 是一個和f 值無關只與a,b 有關的值,那麼每個修改實際上也就是對f(g,g) 的修改。
  設fi=f(i,i),ansi=gcd(a,b)=ik(a,b)fi 。考慮f(a,b)=ab ,則ab=k(a,b)gcd(a,b)2 ,也即k(a,b)=ab/gcd(a,b)2
  因此我們有ansk=fiikjk[gcd(i,j)==1]ij
  然後。。。。。
  如果我們可以O(1) 獲得後面的式子,那麼每次修改可以O(m) 的計算答案。。。
  而這要求我們對O(min(n,m2)) 個可能的k 計算ki=1kj=1[gcd(i,j)==1]ij
  考慮遞推,對於n>1,Sn=Sn1+2ni=1[gcd(i,n)==1]i ,設gn=i[gcd(i,n)==1]i=dnμ(d)d(n/d+1)(n/d)/2 ,這個gn 看起來像是積性函數和非積性函數的狄利克雷卷積,所以我們暴力一下O(nlnn) 地預處理,然後就可以O(n)S(n) ,然後就可以O(m2) 地回答m 個詢問。
  這樣的複雜度是O(nlnn+m2) ,然而由於中間有大量取模/longlong乘法,所以實際效果沒比暴力好多少。。。
  我們想辦法把log去掉,注意對於n>1,gn 實際上就是n2/2dnμ(d)/d ,這樣就可以直接線性篩了。
  複雜度就變成了O(n+m2) 。。。是個能A的非常優秀的暴力算法呀233333333


【代碼】
不要問我爲什麼有些奇怪的無用的東西。。

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (int i = a, _ = b; i <= _; i ++)
#define cerr cerr << ">>> "
const int mod = 1000000007;
const int N = 4000003;

inline int Pow(int a, int b) {
  a %= mod;
  int t = 1;
  while (b) {
    if (b & 1) t = 1ll * t * a % mod;
    a = 1ll * a * a % mod , b >>= 1;
  }
  return t;
}

inline int ars(int n) {
  return 1ll * n * (n + 1) / 2 % mod;
}

int p[N], f[N], vis[N], S[N], pr[N / 10], tot;

int e[N], mn[N], inv[N];

void init(int n) {
  inv[1] = 1;
  rep (i , 2 , n) inv[i] = mod - 1ll * (mod / i) * inv[mod % i] % mod;
  S[1] = 1;
  rep (i , 2 , n) {
    if (!vis[i])
      pr[++ tot] = i, S[i] = mod + 1 - inv[i], mn[i] = i, e[i] = i;
    for (int j = 1; j <= tot && i * pr[j] <= n; j ++) {
      int d = pr[j], t = i * d;
      vis[t] = 1;
      if (i % d == 0) {
        mn[t] = d;
        e[t] = e[i] * d; 
        S[t] = (1ll * S[t / e[t]] - 1ll * inv[d] * S[t / e[t]]) % mod;
        break;
      }
      e[t] = mn[t] = min(mn[i], d);
      S[t] = 1ll * (1 - inv[mn[t]] + mod) * S[i] % mod;
    }
  }
  int inv2 = (mod + 1) / 2;
  rep (i , 2 , n) S[i] = (1ll * S[i] * i % mod * i % mod * inv2 % mod + mod) % mod;
  S[1] = 1;
  rep (i , 2 , n) S[i] = (S[i - 1] + 2ll * i * S[i] % mod) % mod;
  rep (i , 1 , n) f[i] = 1ll * i * i % mod, p[i] = 1;
}

int x[N], g[N];
int n, m;

int gao(int a, int b) {
  if (a > b) swap(a, b);
  if (b % a == 0) return b / a % mod;
  return 1ll * b * Pow(b % a, mod - 2) % mod * gao(a, b % a) % mod;
}

int main() {
  ios::sync_with_stdio(0);
  cin >> m >> n;
  init(n);
  rep (i , 1 , m) {
    int k, a, b;
    long long y;
    cin >> a >> b >> y >> k;
    x[i]  = y % mod;
    g[i]  = __gcd(a, b);
    int tw  = gao(a, b);
    int  w  = 1ll * tw * f[g[i]] % mod;
    f[g[i]] = 1ll * x[i] * Pow(tw, mod - 2) % mod;
    int ans = 1ll * ars(k) * ars(k) % mod;
    x[i] = 1ll * x[i] * Pow(w, mod - 2) % mod;
    w = x[i];
    x[i] = 1ll * (w + mod - 1) * p[g[i]] % mod;
    p[g[i]] = 1ll * p[g[i]] * w % mod;
    rep (j , 1 , i)
      (ans += 1ll * g[j] * g[j] % mod * S[k / g[j]] % mod * x[j] % mod) %= mod;
    if (ans < 0) ans += mod;
    printf("%d\n", ans);
  }
  return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章