zoukankan      html  css  js  c++  java
  • 【SDOI 2017】龙与地下城(组合)

    概率论太难了,不会。但这不能阻止我们过题。
    相信大家都会一个基于背包的暴力做法,我们可以将其看成是卷积的形式就可以用fft优化了。形式化讲,就是求幂级数$ (sumlimits_{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位。

    在模意义下的做法
    如果答案可以对某个质数取模,这题就更好做了。
    考虑最开始的那个幂级数:

    $(sumlimits_{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}$

    手动展开后面的两个:

    $=[z^n] (frac{1}{x})^y(sumlimits_{i = 0}^{y}inom{y}{i}(-1)^iz^{ix})(sumlimits_{i = 0}inom{y + i}{y}z^i)$

    $=(frac{1}{x})^ysumlimits_{i = 0}^{y}inom{y}{i}(-1)^iinom{y + n - ix}{y}$

    预处理组和数之后随便做一下就好了,复杂度$O(x*y)$。

    $igodot$套路与技巧:

    • 隔板法用于展开形如$(frac{1}{1 - z})^y$的幂级数。

    做法一:

    #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
    ", ans[r] - (l? ans[l - 1] : 0));
        }
      }
    
      return 0;
    }
    View Code
  • 相关阅读:
    ASP.NET Web应用程序与ASP.NET Web服务应用程序的区别
    【你必须知道的.NET】:【大话String】
    获取SQLServer数据库中所有表
    Window_Open详解收藏
    关于数据实现批量删除
    asp.net mvc 图形解析说明原理
    【转载】:C#语言
    泛型参数的约束
    SQL 常用函数小结
    [转载]:C#两种不同的存储过程调用方法
  • 原文地址:https://www.cnblogs.com/Dance-Of-Faith/p/10459784.html
Copyright © 2011-2022 走看看