1と17のリビジョン間の差分 (その間の編集: 16回)
2012-06-16 19:20:34時点のリビジョン1
サイズ: 5787
編集者: amylase
コメント:
2013-01-20 12:30:07時点のリビジョン17
サイズ: 14455
編集者: y3eadgbe
コメント:
削除された箇所はこのように表示されます。 追加された箇所はこのように表示されます。
行 3: 行 3:
(last updated: 11/8 20:42)
 * TODO
  * 線型計画を二段階単体法にする。
  * 幾何(線分アレンジメント)
  * 文字列(KMP method, Aho-Corasick algorithm)
  * (ここには載せないが、蟻本から最大流・最小費用流・拡張ユークリッド・連立線型合同式を持っていく)
  * 余裕があれば有名問題を増やす。
   * グラフ彩色
行 7: 行 15:
{{{#!cpp {{{#!highlight cpp
行 30: 行 38:
{{{#!cpp
typedef complex<long double> pt;
typedef pair<pt, pt> line;
typedef pair<pt, long double> circle;
typedef vector<pt> poly;
}}}

 * pt : 点もしくはベクトル。abs(x) で長さ。arg(x) で偏角[-pi, +pi]。a + bi = pt(a, b)
 * line : 線分・直線。通る2点(線分では両端)。
 * circle : 円。中心点と半径。
 * poly : 多角形。反時計回りに頂点を持つ。
{{{#!highlight cpp
// primitives
typedef long double Real;
typedef complex<Real> Pt;
typedef pair<Pt, Pt> Line;
typedef pair<Pt, Real> Circle;
typedef vector<Pt> Poly;

Real eps = 1e-9;

inline istream& operator>>(istream& s, Pt& p) {return s >> p.real() >> p.imag();}
inline bool near(const Pt& p, const Pt& q){return abs(p - q) < eps;}
}}}

 * Real : long double, double の切り替え用に。
 * Pt : 点もしくはベクトル。abs(x) で長さ。arg(x) で偏角[-pi, +pi]。a + bi = pt(a, b)
 * Line : 線分・直線。通る2点(線分では両端)。
 * Circle : 円。中心点と半径。
 * Poly : 多角形。反時計回りに頂点を持つ。
 * eps : 許容誤差。問題によって値を修正する。
 * operator>> : (x座標) (y座標) と来た時に一発で読み取る。
 * near : 誤差を許容して同じ点かどうか見るよ。
行 43: 行 62:
{{{#!cpp
long double inprod(pt a, pt b){
  return a.real() * b.real() + a.imag() * b.imag();
}

long double det(pt p, pt q){
  return p.real() * q.imag() - p.imag() * q.real();
}
}}}

 * inprod : a, b の内積。射影を取ったりするときに使う。
 * det : a, b が張る平行四辺形の面積。三角形の面積とか。
{{{#!highlight cpp
inline Real dot(const Pt& p, const Pt& q){return p.real() * q.real() + p.imag() * q.imag();}
inline Real cross(const Pt& p, const Pt& q){return p.real() * q.imag() - p.imag() * q.real();}
}}}

 * dot : a, b の内積。射影を取ったりするときに使う。
 * cross : a, b が張る平行四辺形の面積。三角形の面積とか。a, b を通る直線上の点 x が cross(x, b) = cross(a, b) を満たすことから、abの法線を軸としたパラメータとの解釈もある。
行 57: 行 71:
{{{#!cpp
long double ccw(pt p, pt q, pt r){
  pt n = (q-p) * pt(0, 1);
  long double c = inprod(n, p);
  return inprod(n, r) - c;
}
}}}

 * ccw > 0 → 反時計回り。
 * ccw < 0 → 時計回り。
 * ccw = 0 → 一直線上にある。

 * 直線と点の位置関係にも使えますよ。
{{{#!highlight cpp
// ccw_b : old version of ccw.
inline Real ccw_b(const Pt& p, const Pt& q, const Pt& r) {return cross(q - p, r - p);}

/* ccw :
CD : counter direction
CW : clock wise
OS : on segment
CCW : counter clock wise
D : direction
 */
enum LPposit { P_CD = -2, P_CW = -1, P_OS = 0, P_CCW = 1, P_D = 2};
LPposit ccw(const Pt& p, const Pt& q, const Pt& r) {
  Real c = ccw_b(p, q, r);
  if (c < -eps) return P_CW;
  if (c > eps) return P_CCW;
  if (dot(q - p, r - p) < -eps) return P_CD;
  if (dot(p - q, r - q) < -eps) return P_D;
  return P_OS;
}
}}}
 * ccw_b : 上で述べた cross のパラメータ解釈での値を計算。
 * ccw : コメント参照!
行 72: 行 96:
{{{#!cpp
long double dist(line l, pt c){
  pt a = l.first, b = l.second;
  return abs(cross(b-a, c-a)) / abs(b-a);
}
}}}

 * 直線 l と点 c の距離。
{{{#!highlight cpp
inline Real Sabs(const Line& l) {return abs(l.first - l.second); }
inline Real LPdist(const Line& l, const Pt& p) {return abs(ccw_b(l.first, l.second, p)) / Sabs(l); }
inline Real SPdist(const Line& l, const Pt& p) {
  if ((Labs(l) > abs(l.first - p) && Labs(l) > abs(l.second - p)) ||
      cos(arg(p - l.first) - arg(p - l.second)) > 0){

    return LPdist(l, p);
  }

  return min(abs(p - l.first), abs(p - l.second));
}
}}}

 * Sabs : 線分の長さ。
 * LPdist : 点と直線の距離。
 * SPdist : 点と線分の距離。
  * L : line, S : segment, P : point
行 82: 行 116:
{{{#!cpp
boo is_cross(line p, line q){
{{{#!highlight cpp
bool crossS(const Line& p, const Line& q){
行 85: 行 119:
    ccw(p.first, q.second, q.first) * ccw(q.first, p.second, q.second) <= 0 &&     ccw(p.first, p.second, q.first) * ccw(p.first, p.second, q.second) <= 0 &&
行 93: 行 127:
{{{#!cpp
pt intersect(line l, line m){
  pt x = l.second - l.first;
  pt y = m.second - m.first;
  pt p = l.first;
  pt c = m.first - l.first;

  double det = x.real() * y.imag() - x.imag() * y.real();
  double k = (y.imag() * c.real() - y.real() * c.imag()) / det;

  return p + k * x;
{{{#!highlight cpp
Pt intersect(const Line& p, const Line& q) {
  Pt vp = p.second - p.first;
  Pt vq = q.second - q.first;
  Pt c(cross(vp, p.first), cross(vq, q.first));
  return Pt(cross(c, Pt(vp.real(), vq.real())), cross(c, Pt(vp.imag(), vq.imag()))) / cross(vp, vq);
行 108: 行 137:
{{{#!cpp
long double area(poly p){
  long double ret = 0.0;
  pt bp = p.back();
  rep(i, p.size()){
    ret += p[i].imag() * bp.real() - p[i].real() * bp.imag();
    bp = p[i];
  }
{{{#!highlight cpp
Real area(const Poly& p) {
  Real ret = 0.0;
  for(Poly::iterator it = p.begin(); it != p.end(); it++) ret += cross(*it, *(it + 1));
行 118: 行 143:
行 123: 行 149:
=== 円と直線の交点 ===
{{{#!highlight cpp
vector<pt> circle_line_intersect(line l,circle c){
  vector<pt> ret;
  LD di = dist(l,c.first);
  LD r=c.second;
  if(di+EPS > r) return ret;
  pt v=(l.second-l.first);
  v/=abs(v);
  pt rv=v*pt(0,1);
  rv*=di;
  if(dist(l,c.first+rv) > di+EPS) rv = -rv;
  v*=sqrt(r*r-di*di);
  ret.push_back(c.first+rv-v);
  ret.push_back(c.first+rv+v);
  return ret;
}
}}}

 * 円と直線の交点を求める。
 * 円が直線と接しない場合はemptyを返し、それ以外は取り敢えず二つの要素を持つvectorを返す。
行 124: 行 172:
{{{#!cpp {{{#!highlight cpp
行 178: 行 226:
=== 凸包 ===
{{{#!highlight cpp
// convex-hull
// return minimum convex polygon that contains every point in vs.
// depend on ccw
namespace std{
  bool operator<(const Pt& a, const Pt& b) {
    return a.real() != b.real() ? a.real() < b.real() : a.imag() < b.imag();
  }
}

Poly convexHull(vector<Pt> ps) {
  int n = ps.size();
  sort(ps.begin(), ps.end());

  Poly ret(2 * n);
  int m = 0;
  for (int i = 0; i < n; i++) {
    while (m >= 2 && ccw(ret[m-2], ret[m-1], ps[i]) < 0) m--;
    ret[m++] = ps[i];
  }

  int t = m;
  for (int i = n-2; i >= 0; i--) {
    while (m >= t && ccw(ret[m-2], ret[m-1], ps[i]) < 0) m--;
    ret[m++] = ps[i];
  }

  ret.resize(m - 1);
  return ret;
}
}}}

 * Pt の座標軸ソートもついてる。
 * convexHull : ps に含まれる点をすべて含む最小の凸多角形を返す。spaghetthi source を参考にしたよ。
行 180: 行 264:
{{{#!cpp {{{#!highlight cpp
行 218: 行 302:
{{{#!cpp {{{#!highlight cpp
行 233: 行 317:

== 数学 ==
=== 行列乗算 ===
{{{#!highlight cpp

typedef vector<vector<li> > matrix;
const li mod = 1000000007;

matrix ident(const int& n) {
    matrix ret(n, vi(n, 0));
    rep(i, n) ret[i][i] = 1;
    return ret;
}

matrix matadd(const matrix& p, const matrix& q) {
    int n = p.size(), m = p[0].size();
    matrix ret = p;
    rep(i, n) rep(j, m) ret[i][j] = (p[i][j] + q[i][j]) % mod;
    return ret;
}

matrix matmul(const matrix& p, const matrix& q) {
    int n = p.size(), m = q[0].size(), l = p[0].size();
    matrix ret = matrix(n, vi(m, 0));
    rep(i, n) rep(j, m) rep(k, l) ret[i][j] = (ret[i][j] + p[i][k] * q[k][j] % mod) % mod;
    return ret;
}

matrix matpow(const matrix& p, const li& x) {
    if (x == 0) return ident(p.size());
    if (x == 1) return p;
    matrix ret = matpow(p, x / 2);
    ret = matmul(ret, ret);
    if (x % 2) ret = matmul(ret, p);
    return ret;
}
}}}
 * 2次元配列で行列を表現。エラーチェックなし。mod とってます。

=== 畳み込み ===
{{{#!highlight cpp

typedef double Real;
typedef complex<Real> Complex;

vector<Complex> fastFourierTransform(const vector<Complex>& x) {
    if (__builtin_popcount(x.size()) > 1) {
        cerr << "fastFourierTransform: x.size() should be power of 2." << endl;
        return x;
    }

    const int n = x.size();
    if (n == 1) {
        return x;
    }

    vector<Complex> even(n / 2), odd(n / 2), ret(n);
    for (int i = 0; i < n; ++i) {
        if (i % 2 == 0) {
            even[i / 2] = x[i];
        } else {
            odd[i / 2] = x[i];
        }
    }

    even = fastFourierTransform(even);
    odd = fastFourierTransform(odd);

    for (int i = 0; i < n; ++i) {
        int j = i % (n / 2);
        ret[i] = even[j] + odd[j] * exp(Complex(0, -2 * M_PI * i / n));
    }

    return ret;
}

vector<Complex> inverseFastFourierTransform(vector<Complex> x) {
    const int n = x.size();
    for (int i = 0; i < n; ++i) {
        x[i] = conj(x[i]);
    }
    x = fastFourierTransform(x);
    for (int i = 0; i < n; ++i) {
        x[i] = conj(x[i]) / Complex(n);
    }
    return x;
}

int conv_minpow2(int x) {
    int ret = 1;
    while (ret < x) ret <<= 1;
    return ret;
}

vector<Complex> convolution(vector<Complex> x,
                            vector<Complex> y) {
    int l = 2 * max(conv_minpow2(x.size()), conv_minpow2(y.size()));
    x.resize(l);
    y.resize(l);

    x = fastFourierTransform(x);
    y = fastFourierTransform(y);

    vector<Complex> ret(l);
    for (int i = 0; i < l; i++) {
        ret[i] = x[i] * y[i];
    }

    ret = inverseFastFourierTransform(ret);
    while (not ret.empty() && ret.back() == Complex(0)) ret.pop_back();
    return ret;
}
}}}
 * 高速フーリエ変換による畳み込み演算。O(n log n)。
 * 多項式の掛け算がナイーブにやるとO(n^2)だが畳込みを使うと計算量が落ちたり。

== 有名問題 ==
=== 線型計画法 ===
{{{#!highlight cpp
// linear programming by simplex method
// solve maximize(cx) s.t. Ax <= a
// if not bounded, return -1.
double simplex(vector<double>& c,
               vector<vector<double> >& A,
               vector<double>& a) {
  // introduce slack variable
  int n = c.size(), m = a.size(), dim = n + m;
  vector<int> N(n), B(m);
  for (int i = 0; i < n; ++i) N[i] = i;
  for (int i = 0; i < m; ++i) {
    B[i] = i + n;
    c.push_back(0);
    for (int j = 0; j < n; j++) A[i][j] *= -1;
    for (int j = 0; j < m; j++) A[i].push_back(0);
  }

  double ans = 0;

  while (true) {
    // check optimized or not
    int s = -1;
    for (int i = 0; i < n; i++) if (c[N[i]] > 0) s = N[i];
    if (s < 0) break;

    // check bounded or not
    double bound = 1e300;
    int r = -1;
    for (int i = 0; i < m; i++) {
      if (A[i][N[s]] < 0) {
        double nbound = -a[i] / A[i][N[s]];
        if (nbound < bound) {
          bound = nbound;
          r = i;
        }
      }
    }

    if (r < 0) return -1;

    // pivotting
    for (int i = 0; i < dim; i++) if (i != N[s]) A[r][i] /= -A[r][N[s]];
    a[r] /= -A[r][N[s]];
    A[r][B[r]] = 1.0 / A[r][N[s]];
    A[r][N[s]] = 0;

    for (int i = 0; i < m; i++) {
      if (i == r) continue;

      for (int j = 0; j < dim; j++) if (j != N[s]) A[i][j] += A[i][N[s]] * A[r][j];
      a[i] += A[i][N[s]] * a[r];
      A[i][N[s]] = 0;
    }

    ans += c[N[s]] * a[r];
    for (int i = 0; i < dim; i++) if (i != N[s]) c[i] += c[N[s]] * A[r][i];
    c[N[s]] = 0;

    swap(N[s], B[r]);
  }

  return ans;
}
}}}
 * simplex(c, A, a) : maximize cx, where Ax <= a, x >= 0 なる線型計画問題を単体法で解く。
 * x = 0 が実行可能解であることを仮定している(つまり、TODOとして二段階単体法に改善することがあげられる)。
 * 発散する場合には -1 を返す。有限な最適解は必ず正になっているはずなので、正負を見れば発散したかどうかがわかる。

++w++ ICPCライブラリ

++w++(amylase, y3eadgbe, atetubou(is2012))で使用する ICPC ライブラリ置き場。 (last updated: 11/8 20:42)

  • TODO
    • 線型計画を二段階単体法にする。
    • 幾何(線分アレンジメント)
    • 文字列(KMP method, Aho-Corasick algorithm)
    • (ここには載せないが、蟻本から最大流・最小費用流・拡張ユークリッド・連立線型合同式を持っていく)
    • 余裕があれば有名問題を増やす。
      • グラフ彩色

テンプレート

   1 #include <iostream>
   2 #include <vector>
   3 #include <map>
   4 #include <algorithm>
   5 #include <queue>
   6 #include <cstring>
   7 #include <complex>
   8 
   9 using namespace std;
  10 
  11 #define rep(i,n) for(int i=0;i<n;++i)
  12 #define pb(a) push_back(a)
  13 #define mp(a,b) make_pair(a,b)
  14 #define F first
  15 #define S second
  16 #define SZ(a) (int)((a).size())
  17 
  • だいたいコードの先頭にあると考えておいてね。

幾何

基本要素

   1 // primitives
   2 typedef long double Real;
   3 typedef complex<Real> Pt;
   4 typedef pair<Pt, Pt> Line;
   5 typedef pair<Pt, Real> Circle;
   6 typedef vector<Pt> Poly;
   7 
   8 Real eps = 1e-9;
   9 
  10 inline istream& operator>>(istream& s, Pt& p) {return s >> p.real() >> p.imag();}
  11 inline bool near(const Pt& p, const Pt& q){return abs(p - q) < eps;}
  • Real : long double, double の切り替え用に。
  • Pt : 点もしくはベクトル。abs(x) で長さ。arg(x) で偏角[-pi, +pi]。a + bi = pt(a, b)
  • Line : 線分・直線。通る2点(線分では両端)。
  • Circle : 円。中心点と半径。
  • Poly : 多角形。反時計回りに頂点を持つ。
  • eps : 許容誤差。問題によって値を修正する。
  • operator>> : (x座標) (y座標) と来た時に一発で読み取る。

  • near : 誤差を許容して同じ点かどうか見るよ。

内積・外積の大きさ

   1 inline Real dot(const Pt& p, const Pt& q){return p.real() * q.real() + p.imag() * q.imag();}
   2 inline Real cross(const Pt& p, const Pt& q){return p.real() * q.imag() - p.imag() * q.real();}
  • dot : a, b の内積。射影を取ったりするときに使う。
  • cross : a, b が張る平行四辺形の面積。三角形の面積とか。a, b を通る直線上の点 x が cross(x, b) = cross(a, b) を満たすことから、abの法線を軸としたパラメータとの解釈もある。

3点の位置関係

   1 // ccw_b : old version of ccw.
   2 inline Real ccw_b(const Pt& p, const Pt& q, const Pt& r) {return cross(q - p, r - p);}
   3 
   4 /* ccw :
   5 CD  : counter direction
   6 CW  : clock wise
   7 OS  : on segment
   8 CCW : counter clock wise
   9 D   : direction
  10  */
  11 enum LPposit { P_CD = -2, P_CW = -1, P_OS = 0, P_CCW = 1, P_D = 2};
  12 LPposit ccw(const Pt& p, const Pt& q, const Pt& r) {
  13   Real c = ccw_b(p, q, r);
  14   if (c < -eps) return P_CW;
  15   if (c >  eps) return P_CCW;
  16   if (dot(q - p, r - p) < -eps) return P_CD;
  17   if (dot(p - q, r - q) < -eps) return P_D;
  18   return P_OS;
  19 }
  • ccw_b : 上で述べた cross のパラメータ解釈での値を計算。
  • ccw : コメント参照!

点と直線の距離

   1 inline Real Sabs(const Line& l) {return abs(l.first - l.second); }
   2 inline Real LPdist(const Line& l, const Pt& p) {return abs(ccw_b(l.first, l.second, p)) / Sabs(l); }
   3 inline Real SPdist(const Line& l, const Pt& p) {
   4   if ((Labs(l) > abs(l.first - p) && Labs(l) > abs(l.second - p)) ||
   5       cos(arg(p - l.first) - arg(p - l.second)) > 0){
   6 
   7     return LPdist(l, p);
   8   }
   9 
  10   return min(abs(p - l.first), abs(p - l.second));
  11 }
  • Sabs : 線分の長さ。
  • LPdist : 点と直線の距離。
  • SPdist : 点と線分の距離。
    • L : line, S : segment, P : point

線分の交差判定

   1 bool crossS(const Line& p, const Line& q){
   2   return
   3     ccw(p.first, p.second, q.first) * ccw(p.first, p.second, q.second) <= 0 &&
   4     ccw(q.first, q.second, p.first) * ccw(q.first, q.second, p.second) <= 0;
   5 }
  • 線分 p と q が交差するかどうか。

直線の交点

   1 Pt intersect(const Line& p, const Line& q) {
   2   Pt vp = p.second - p.first;
   3   Pt vq = q.second - q.first;
   4   Pt c(cross(vp, p.first), cross(vq, q.first));
   5   return Pt(cross(c, Pt(vp.real(), vq.real())), cross(c, Pt(vp.imag(), vq.imag()))) / cross(vp, vq);
   6 }

多角形の面積

   1 Real area(const Poly& p) {
   2   Real ret = 0.0;
   3   for(Poly::iterator it = p.begin(); it != p.end(); it++) ret += cross(*it, *(it + 1));
   4   return ret / 2;
   5 }
  • 多角形の面積。凸性は仮定しない。O(n)。
  • 時計回りの時は面積が -1 倍される。abs 取ろう。

円と直線の交点

   1 vector<pt> circle_line_intersect(line l,circle c){
   2   vector<pt> ret;
   3   LD di = dist(l,c.first);
   4   LD r=c.second;
   5   if(di+EPS > r) return ret;
   6   pt v=(l.second-l.first);
   7   v/=abs(v);  
   8   pt rv=v*pt(0,1);
   9   rv*=di;  
  10   if(dist(l,c.first+rv) > di+EPS) rv = -rv;
  11   v*=sqrt(r*r-di*di);
  12   ret.push_back(c.first+rv-v);
  13   ret.push_back(c.first+rv+v);
  14   return ret;
  15 }
  • 円と直線の交点を求める。
  • 円が直線と接しない場合はemptyを返し、それ以外は取り敢えず二つの要素を持つvectorを返す。

円の共通接線

   1 vector<line> contact(circle p, circle q){
   2   vector<line> ret;
   3   if(p.second < q.second) swap(p, q);
   4   long double d = abs(p.first - q.first);
   5   pt n = q.first - p.first;
   6   n /= abs(n);
   7 
   8   if(d + eps < abs(p.second - q.second)){
   9     ret.clear();
  10   } else if(eq(d, abs(p.second - q.second))){
  11     pt t, u;
  12     t = p.first + p.second * n;
  13     u = t + n * pt(0, 1);
  14     ret.push_back(make_pair(t, u));
  15   } else {
  16     if(!eq(p.second, q.second)){
  17       pt t = p.first + (p.second * d / (p.second - q.second)) * n;
  18       long double theta = asin((p.second - q.second) / d);
  19       pt u = n * pt(cos(theta), sin(theta));
  20       pt v = n * pt(cos(-theta), sin(-theta));
  21       u += t;
  22       v += t;
  23       ret.push_back(make_pair(t, u));
  24       ret.push_back(make_pair(t, v));
  25     } else {
  26       pt t = p.first + n * pt(0, 1) * p.second;
  27       pt u = p.first - n * pt(0, 1) * p.second;
  28       ret.push_back(make_pair(t, t+n));
  29       ret.push_back(make_pair(u, u+n));
  30     }
  31 
  32     if(eq(d, p.second + q.second)){
  33       pt t, u;
  34       t = p.first + p.second * n;
  35       u = t + n * pt(0, 1);
  36       ret.push_back(make_pair(t, u));
  37     } else if(d > p.second + q.second){
  38       pt t = p.first + (p.second * d / (p.second + q.second)) * n;
  39       long double theta = asin((p.second + q.second) / d);
  40       pt u = n * pt(cos(theta), sin(theta));
  41       pt v = n * pt(cos(-theta), sin(-theta));
  42       u += t;
  43       v += t;
  44       ret.push_back(make_pair(t, u));
  45       ret.push_back(make_pair(t, v));
  46     }
  47   }
  48   return ret;
  49 }
  • vector<line> の各要素が接線。

凸包

   1 // convex-hull                                                                  
   2 // return minimum convex polygon that contains every point in vs.               
   3 // depend on ccw                                                                
   4 namespace std{
   5   bool operator<(const Pt& a, const Pt& b) {
   6     return a.real() != b.real() ? a.real() < b.real() : a.imag() < b.imag();
   7   }
   8 }
   9 
  10 Poly convexHull(vector<Pt> ps) {
  11   int n = ps.size();
  12   sort(ps.begin(), ps.end());
  13 
  14   Poly ret(2 * n);
  15   int m = 0;
  16   for (int i = 0; i < n; i++) {
  17     while (m >= 2 && ccw(ret[m-2], ret[m-1], ps[i]) < 0) m--;
  18     ret[m++] = ps[i];
  19   }
  20 
  21   int t = m;
  22   for (int i = n-2; i >= 0; i--) {
  23     while (m >= t && ccw(ret[m-2], ret[m-1], ps[i]) < 0) m--;
  24     ret[m++] = ps[i];
  25   }
  26 
  27   ret.resize(m - 1);
  28   return ret;
  29 }
  • Pt の座標軸ソートもついてる。
  • convexHull : ps に含まれる点をすべて含む最小の凸多角形を返す。spaghetthi source を参考にしたよ。

数論

ミラー・ラビン素数判定法

   1 long long powmod(long long x, long long p, long long m){
   2   if(p == 0) return 1;
   3   long long rt = powmod(x, p/2, m);
   4   if(p % 2 == 0){
   5     return rt * rt % m;
   6   } else {
   7     return (rt * rt % m) * x % m;
   8   }
   9 }
  10 
  11 int miller_test[9] = {2,3,5,7,11,13,17,19,23};
  12 bool isprime(long long n){
  13   if(n <= ISPR_MAX) return !isnpr[n];
  14 
  15   long long d = n-1, s = 0;
  16   while(d%2 == 0){
  17     d /= 2;
  18     s++;
  19   }
  20 
  21   for(int i=0; i<9; i++){
  22     bool iscomp = true;
  23     li x = powmod(miller_test[i], d, n);
  24     iscomp = iscomp && (x % n != 1);
  25 
  26     for(int r=0; r<s; r++){
  27       iscomp = iscomp && (x % n != n-1);
  28       x = x * x % n;
  29     }
  30     if(iscomp) return false;
  31   }
  32 
  33   return true;
  34 }

ポラード・ロー素因数分解法

   1 long long myrand(long long c, long long n, long long x){
   2   return (x * x + c) % n;
   3 }
   4  
   5 long long pollard(long long n){
   6   long long x = 2, y = 2, d = 1, c = rand();
   7   while(d == 1){
   8     x = myrand(c, n, x);
   9     y = myrand(c, n, myrand(c, n, y));
  10     d = __gcd(abs(x-y), n);
  11   }
  12   return d;
  13 }

数学

行列乗算

   1 typedef vector<vector<li> > matrix;
   2 const li mod = 1000000007;
   3 
   4 matrix ident(const int& n) {
   5     matrix ret(n, vi(n, 0));
   6     rep(i, n) ret[i][i] = 1;
   7     return ret;
   8 }
   9 
  10 matrix matadd(const matrix& p, const matrix& q) {
  11     int n = p.size(), m = p[0].size();
  12     matrix ret = p;
  13     rep(i, n) rep(j, m) ret[i][j] = (p[i][j] + q[i][j]) % mod;
  14     return ret;
  15 }
  16 
  17 matrix matmul(const matrix& p, const matrix& q) {
  18     int n = p.size(), m = q[0].size(), l = p[0].size();
  19     matrix ret = matrix(n, vi(m, 0));
  20     rep(i, n) rep(j, m) rep(k, l) ret[i][j] = (ret[i][j] + p[i][k] * q[k][j] % mod) % mod;
  21     return ret;
  22 }
  23 
  24 matrix matpow(const matrix& p, const li& x) {
  25     if (x == 0) return ident(p.size());
  26     if (x == 1) return p;
  27     matrix ret = matpow(p, x / 2);
  28     ret = matmul(ret, ret);
  29     if (x % 2) ret = matmul(ret, p);
  30     return ret;
  31 }
  • 2次元配列で行列を表現。エラーチェックなし。mod とってます。

畳み込み

   1 typedef double Real;
   2 typedef complex<Real> Complex;
   3 
   4 vector<Complex> fastFourierTransform(const vector<Complex>& x) {
   5     if (__builtin_popcount(x.size()) > 1) {
   6         cerr << "fastFourierTransform: x.size() should be power of 2." << endl;
   7         return x;
   8     }
   9 
  10     const int n = x.size();
  11     if (n == 1) {
  12         return x;
  13     }
  14 
  15     vector<Complex> even(n / 2), odd(n / 2), ret(n);
  16     for (int i = 0; i < n; ++i) {
  17         if (i % 2 == 0) {
  18             even[i / 2] = x[i];
  19         } else {
  20             odd[i / 2] = x[i];
  21         }
  22     }
  23 
  24     even = fastFourierTransform(even);
  25     odd = fastFourierTransform(odd);
  26 
  27     for (int i = 0; i < n; ++i) {
  28         int j = i % (n / 2);
  29         ret[i] = even[j] + odd[j] * exp(Complex(0, -2 * M_PI * i / n));
  30     }
  31 
  32     return ret;
  33 }
  34 
  35 vector<Complex> inverseFastFourierTransform(vector<Complex> x) {
  36     const int n = x.size();
  37     for (int i = 0; i < n; ++i) {
  38         x[i] = conj(x[i]);
  39     }
  40     x = fastFourierTransform(x);
  41     for (int i = 0; i < n; ++i) {
  42         x[i] = conj(x[i]) / Complex(n);
  43     }
  44     return x;
  45 }
  46 
  47 int conv_minpow2(int x) {
  48     int ret = 1;
  49     while (ret < x) ret <<= 1;
  50     return ret;
  51 }
  52 
  53 vector<Complex> convolution(vector<Complex> x,
  54                             vector<Complex> y) {
  55     int l = 2 * max(conv_minpow2(x.size()), conv_minpow2(y.size()));
  56     x.resize(l);
  57     y.resize(l);
  58 
  59     x = fastFourierTransform(x);
  60     y = fastFourierTransform(y);
  61 
  62     vector<Complex> ret(l);
  63     for (int i = 0; i < l; i++) {
  64         ret[i] = x[i] * y[i];
  65     }
  66 
  67     ret = inverseFastFourierTransform(ret);
  68     while (not ret.empty() && ret.back() == Complex(0)) ret.pop_back();
  69     return ret;
  70 }
  • 高速フーリエ変換による畳み込み演算。O(n log n)。
  • 多項式の掛け算がナイーブにやるとO(n^2)だが畳込みを使うと計算量が落ちたり。

有名問題

線型計画法

   1 // linear programming by simplex method
   2 // solve maximize(cx) s.t. Ax <= a
   3 // if not bounded, return -1.
   4 double simplex(vector<double>& c, 
   5                vector<vector<double> >& A,
   6                vector<double>& a) {
   7   // introduce slack variable
   8   int n = c.size(), m = a.size(), dim = n + m;
   9   vector<int> N(n), B(m);
  10   for (int i = 0; i < n; ++i) N[i] = i;
  11   for (int i = 0; i < m; ++i) {
  12     B[i] = i + n;
  13     c.push_back(0);
  14     for (int j = 0; j < n; j++) A[i][j] *= -1;
  15     for (int j = 0; j < m; j++) A[i].push_back(0);
  16   }
  17 
  18   double ans = 0;
  19 
  20   while (true) {
  21     // check optimized or not
  22     int s = -1;
  23     for (int i = 0; i < n; i++) if (c[N[i]] > 0) s = N[i];
  24     if (s < 0) break;
  25 
  26     // check bounded or not
  27     double bound = 1e300;
  28     int r = -1;
  29     for (int i = 0; i < m; i++) {
  30       if (A[i][N[s]] < 0) {
  31         double nbound = -a[i] / A[i][N[s]];
  32         if (nbound < bound) {
  33           bound = nbound;
  34           r = i;
  35         }
  36       }
  37     }
  38 
  39     if (r < 0) return -1;
  40 
  41     // pivotting
  42     for (int i = 0; i < dim; i++) if (i != N[s]) A[r][i] /= -A[r][N[s]];
  43     a[r] /= -A[r][N[s]];
  44     A[r][B[r]] = 1.0 / A[r][N[s]];
  45     A[r][N[s]] = 0;
  46 
  47     for (int i = 0; i < m; i++) {
  48       if (i == r) continue;
  49 
  50       for (int j = 0; j < dim; j++) if (j != N[s]) A[i][j] += A[i][N[s]] * A[r][j];
  51       a[i] += A[i][N[s]] * a[r];
  52       A[i][N[s]] = 0;
  53     }
  54 
  55     ans += c[N[s]] * a[r];
  56     for (int i = 0; i < dim; i++) if (i != N[s]) c[i] += c[N[s]] * A[r][i];
  57     c[N[s]] = 0;
  58 
  59     swap(N[s], B[r]);
  60   }
  61 
  62   return ans;
  63 }
  • simplex(c, A, a) : maximize cx, where Ax <= a, x >= 0 なる線型計画問題を単体法で解く。

  • x = 0 が実行可能解であることを仮定している(つまり、TODOとして二段階単体法に改善することがあげられる)。
  • 発散する場合には -1 を返す。有限な最適解は必ず正になっているはずなので、正負を見れば発散したかどうかがわかる。

amylase/icpc (最終更新日時 2014-05-21 21:03:43 更新者 amylase)