【SDOI 2017】龍與地下城(組合)

機率論太難了,不會。但這不能阻止咱們過題。
相信你們都會一個基於揹包的暴力作法,咱們能夠將其當作是卷積的形式就能夠用fft優化了。形式化講,就是求冪級數$ (\sum\limits_{i = 0}^{x - 1} \frac{1}{x} z^i)^y $在$[z^A, z^B]$之間的係數和。

不在模意義下的作法
直接將上述冪級數暴力倍增卷積求出來複雜度是$O(x*y*log(xy))$,不太能過的。但若是不在模意義下作咱們就能夠嘗試爆精度爆過去。很容易發現最後求得的點數和很大機率就是在均值附近的,過大太小的機率幾乎爲0。也就是咱們中間在作卷積和有不少項幾乎爲0,咱們把他們忽略掉是在精度的承受範圍以內的。因而咱們在作倍增求出上述冪級數的時候咱們能夠每次只保留中間的一小段有值的部分,其他的扔掉就好了。也就是說咱們設定一個閾值$\epsilon$,每次卷積後把多項式中值$\le \epsilon$的都扔掉。這樣複雜度就是$O(len*log(len + y))$,其中$len$是最終冪級數中值$> \epsilon$的個數。實踐證實,當$\epsilon$取$10^{-9}$時,$len$大概是兩三萬,而且此時的精度能夠達到小數點後4位。

在模意義下的作法
若是答案能夠對某個質數取模,這題就更好作了。
考慮最開始的那個冪級數:

$(\sum\limits_{i = 0}^{x - 1} \frac{1}{x} z^i)^y = (\frac{1}{x} \frac{1 - z^x}{1 - z})^y = (\frac{1}{x})^y (1 - z^x)^y(\frac{1}{1 - z})^y$

咱們想要求其在第$[A, B]$之間的係數和,咱們能夠乘上一個$\frac{1}{1 - x}$來作一次前綴和,這樣咱們能夠轉化成兩次形如求一個$z^n$的係數的問題。也就是:

$[z^n] (\frac{1}{x})^y (1 - z^x)^y(\frac{1}{1 - z})^{y + 1}$

手動展開後面的兩個:c++

$=[z^n] (\frac{1}{x})^y(\sum\limits_{i = 0}^{y}\binom{y}{i}(-1)^iz^{ix})(\sum\limits_{i = 0}\binom{y + i}{y}z^i)$

$=(\frac{1}{x})^y\sum\limits_{i = 0}^{y}\binom{y}{i}(-1)^i\binom{y + n - ix}{y}$ide

預處理組和數以後隨便作一下就行了,複雜度$O(x*y)$。優化

 

$\bigodot$套路與技巧:spa

  • 隔板法用於展開形如$(\frac{1}{1 - z})^y$的冪級數。

 

作法一:3d

#include <bits/stdc++.h>

using namespace std;

namespace PO {
  const int N = 4e5 + 5;
  const double PI = acos(-1);
  
  struct Com {
    double x, y;
    friend Com operator + (Com a, Com b) {
      return (Com){ a.x + b.x, a.y + b.y };
    }
    friend Com operator - (Com a, Com b) {
      return (Com){ a.x - b.x, a.y - b.y };
    }
    friend Com operator * (Com a, Com b) {
      return (Com){ a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x };
    }
  } ta[N], tb[N], w[N];
  int rev[N], L = 1;
  typedef vector<Com> Poly;

  void Init(int l) {
    for (; L < l; L <<= 1) {
      for (int i = L; i < (L << 1); ++i) {
        w[i] = (Com){ cos(PI / L * (i - L)), sin(PI / L * (i - L)) };
      }
    }
    for (int i = 0; i < l; ++i) {
      rev[i] = (rev[i >> 1] >> 1) | (i & 1? l >> 1 : 0);
    }
  }
  
  void Dft(Com *a, int l) {
    for (int i = 0; i < l; ++i)
      if (i < rev[i]) swap(a[i], a[rev[i]]);
    for (int i = 1; i < l; i <<= 1) {
      for (int j = 0; j < l; j += i << 1) {
        Com *l = a + j, *r = l + i, *wx = w + i, y;
        for (int k = 0; k < i; ++k, ++l, ++r, ++wx) {
          y = (*r) * (*wx);
          *r = (*l) - y;
          *l = (*l) + y;
        }
      }
    }
  }
  void Idft(Com *a, int l) {
    reverse(a + 1, a + l);
    Dft(a, l);
    for (int i = 0; i < l; ++i) {
      a[i].x /= l;
      a[i].y /= l;
    }
  }

  vector<double> Mul(vector<double> a, vector<double> b) {
    int n = a.size(), m = b.size(), l;
    for (l = 1; l < n + m - 1; l <<= 1);
    Init(l);
    a.resize(l), b.resize(l);
    for (int i = 0; i < l; ++i) {
      ta[i] = (Com){ a[i], 0 };
      tb[i] = (Com){ b[i], 0 };
    }
    Dft(ta, l), Dft(tb, l);
    for (int i = 0; i < l; ++i) {
      ta[i] = ta[i] * tb[i];
    }
    Idft(ta, l);
    for (int i = 0; i < l; ++i) {
      a[i] = ta[i].x;
    }
    a.resize(n + m - 1);
    return a;
  }
  
}

void Cut(vector<double> &a, int &base) {
  const double GAMA = 1e-9;
  int id = 0;
  while (a[id] < GAMA) ++id;
  for (int i = 0; i + id < a.size(); ++i) {
    a[i] = a[i + id];
  }
  while (a.back() < GAMA) {
    a.pop_back();
  }
  base += id;
}

int main() {
  int tc;
  for (cin >> tc; tc--; ) {
    int x, y;
    cin >> x >> y;

    vector<double> ans(x * y);
    vector<double> F(x, 1.0 / x), G(1, 1);
    int base_f = 0, base_g = 0;
    for (int ex = y; ex; ex >>= 1) {
      if (ex & 1) {
        G = PO::Mul(G, F);
        base_g += base_f;
        Cut(G, base_g);
      }
      F = PO::Mul(F, F);
      base_f *= 2;
      Cut(F, base_f);
    }
    for (int i = 0; i < G.size(); ++i) {
      ans[i + base_g] = G[i];
    }
    for (int i = 1; i < x * y; ++i) {
      ans[i] += ans[i - 1];
    }
    
    for (int cas = 10; cas--; ) {
      int l, r;
      cin >> l >> r;
      printf("%.12f\n", ans[r] - (l? ans[l - 1] : 0));
    }
  }

  return 0;
}
View Code
相關文章
相關標籤/搜索