loj 6051 "Yali training 2017 Day11" PATH - polynomial - hook formula

Topic Portal

  Portal

  设 $m = \sum_{i = 1}^{n} a_i$。

  Obviously the total number of programs is equal to $ \ frac {m!} {\ Prod_ {i = 1} ^ {n} a_i!} $.

  Consider a trellis diagram, the line has $ I $ $ $ a_i grids.

  Then we fill in the grid $ 1 $ to $ m $, if ensure that each row strictly increasing, then the first $ j $-dimensional coordinate is the first $ i $ row after the first $ i move $ times less than equal to $ i $ the number of number.

  Thus a unique path may correspond to one kind of filling process.

  Any point in the path satisfying the condition, is equivalent to the requirements of each column is incremented.

  This is equivalent to the shape of the table given Yang, Yang asked the number of standard table of conditions are met.

  According hook formula, we have

$$
\frac{m!}{\prod_{1 \leqslant i\leqslant n, 1\leqslant j\leqslant a_i} h(i, j)}
$$

  Where $ h (i, j) $, $ represents hook length of line I $, $ J $ column of the lattice.

  It is a positive number equal to the lattice grid below and just to the right and add one.

  This is still hard to deal with.

  Noting the hooks of each row mutually different length, and $ [1, a_i - i + n] $ into.

  Consider the long absence of the hook removed.

  Consider enumeration $ j $ in a row below the first row $ i $, then hook long $ (j - i) + (a_i - a_j) $ does not exist.

  Because when $ j $ increments, $ a_j $ gain, so get rid of the long hook is also different from each other, we will remove a total of $ n - i $ a long hook.

  Thus equation can be transformed into

$$
\frac{m! \prod_{1\leqslant i < j\leqslant n} [(a_i - i) - (a_j - j)]}{\prod_{i = 1}^{n} (a_i - i + n)!}
$$

  So have:

$$
ans = \prod_{i = 1}^{n} \frac{a_i!}{(a_i - i + n)!} \prod_{i \leqslant i < j \leqslant n}[(a_i - i) - (a_j - j)]
$$

  The question now converted into the right half of the calculation.

  It is not difficult to note that $ (a_i - i) - (a_j - j) $ is not too large, and always positive.

  So consider the direct calculation of how many times each value appears.

  This is the basis for NTT operation.

  Then it done.

Code

/**
 * loj
 * Problem#6051
 * Accepted
 * Time: 4126ms
 * Memory: 51352k
 */
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

#define ll long long

const int Mod = 1004535809;
const int N = 1 << 22;
const int bzmax = 23;
const int g = 3;

void exgcd(int a, int b, int& x, int& y) {
	if (!b) {
		x = 1, y = 0;
	} else {
		exgcd(b, a % b, y, x);
		y -= (a / b) * x;
	}
}

int inv(int a) {
	int x, y;
	exgcd(a, Mod, x, y);
	return (x < 0) ? (x + Mod) : (x);
}

template <const int Mod = :: Mod>
class Z {
	public:
		int v;
		
		Z() : v(0) {	}
		Z(int v) : v(v) {	}
		Z(ll x) : v(x % Mod) {	}
		
		Z operator + (Z b) {
			int x = v + b.v;
			return Z((x >= Mod) ? (x - Mod) : (x));
		}
		Z operator - (Z b) {
			int x = v - b.v;
			return Z((x < 0) ? (x + Mod) : (x));
		}
		Z operator * (Z b) {
			return Z(1ll * v * b.v);
		}
		Z operator ~ () {
			return inv(v);
		}
		Z operator -() {
			return Z(0) - *this;
		}
		
		Z& operator += (Z b) {
			return *this = *this + b;
		}
		Z& operator -= (Z b) {
			return *this = *this - b;
		}
		Z& operator *= (Z b) {
			return *this = *this * b;
		}

//		constexpr operator int () const {
//			return v;
//		}
};

typedef Z<> Zi;

Zi qpow(Zi a, int p) {
	if (p < Mod - 1)
		p += Mod - 1;
	Zi rt = 1, pa = a;
	for ( ; p; p >>= 1, pa = pa * pa) {
		if (p & 1) {
			rt = rt * pa;
		}
	}
	return rt;
}

class NTT {
	private:
		Zi gn[bzmax + 4], _gn[bzmax + 4];
	public:
		
		NTT() {
			for (int i = 0; i <= bzmax; i++) {
				gn[i] = qpow(Zi(g), (Mod - 1) >> i);
				_gn[i] = qpow(Zi(g), -((Mod - 1) >> i));
			}
		}

		void operator () (Zi* f, int len, int sgn) {
			for (int i = 1, j = len >> 1, k; i < len - 1; i++, j += k) {
				if (i < j)
					swap(f[i], f[j]);
				for (k = len >> 1; k <= j; j -= k, k >>= 1);
			}
			
			Zi *wn = (sgn > 0) ? (gn + 1) : (_gn + 1), w, a, b;
			for (int l = 2, hl; l <= len; l <<= 1, wn++) {
				hl = l >> 1, w = 1;
				for (int i = 0; i < len; i += l, w = 1) {
					for (int j = 0; j < hl; j++, w *= *wn) {
						a = f[i + j], b = f[i + j + hl] * w;
						f[i + j] = a + b;
						f [i + j + hl]
					}
				}
			}

			if (sgn < 0) {
				Zi invlen = ~Zi(len);
				for (int i = 0; i < len; i++) {
					f[i] *= invlen;
				}
			}
		}

		int correct_len(int len) {
			int m = 1;
			for ( ; m <= len; m <<= 1);
			return m;
		}
} NTT;

const int inf = (signed) (~0u >> 1);

int n;
Zi a[N], b[N];
int A[500005];
Zi fac[N >> 1], _fac[N >> 1];

void init_fac(int n) {
	fac[0] = 1;
	for (int i = 1; i <= n; i++)
		fac[i] = fac[i - 1] * i;
	_fac[n] = ~fac[n];
	for (int i = n; i; i--)
		_fac[i - 1] = _fac[i] * i;
}

int main() {
	scanf("%d", &n);
	int mi = inf, mx = -inf;
	for (int i = 1; i <= n; i++) {
		scanf("%d", A + i);
		mi = min(mi, A[i] - i);
		mx = max(mx, A[i] - i);
	}
	int L = mx - mi + 1, t = NTT.correct_len(L << 1);
	for (int i = 1; i <= n; i++) {
		a[A[i] - i - mi] += 1;
		b[mx - A[i] + i] += 1;
	}
	NTT(a, t, 1);
	NTT(b, t, 1);
	for (int i = 0; i < t; i++)
		a[i] *= b[i];
	NTT(a, t, -1);
	init_fac(mx + n);
	Zi ans = 1;
	for (int i = 1; i <= n; i++)
		ans *= fac[A[i]] * _fac[A[i] - i + n];
	mi -= mx;
	for (int i = 0; i < t; i++) {
		if (i + mi > 0 && a[i].v) {
			ans *= qpow(i + mi, a[i].v);
		}
	}
	printf("%d\n", ans.v);
	return 0;
}

Guess you like

Origin www.cnblogs.com/yyf0309/p/11320531.html