• loj 6051 「雅礼集训 2017 Day11」PATH


    题目传送门

      传送门

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

      总方案数显然等于 $frac{m!}{prod_{i = 1}^{n} a_i!}$。

      考虑这样一个网格图,第 $i$ 行有 $a_i$ 个网格。

      那么我们在这个网格中填 $1$ 到 $m$ ,如果保证每一行严格递增,那么第 $i$ 次移动后第 $j$ 维坐标就是第 $i$ 行中小于等于 $i$ 的数数量。

      因此一条路径可以唯一对应一种填法。

      路径中任意一个点都满足条件,等价于要求每一列递增。

      这等价于给定杨表的形状,问满足条件的标准杨表的数量。

      根据钩子公式,我们有

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

      其中 $h(i, j)$,表示第 $i$ 行,第 $j$ 列的格子的勾长。

      它等于这个格子正下方和正右方的格子数再加一。

      这个仍然不好处理。

      注意到每一行的钩长互不相同,并且在 $[1, a_i - i + n]$ 之中。

      考虑把不存在的钩长除掉。

      考虑枚举在第 $i$ 行下方的一行 $j$,那么钩长 $(j - i) + (a_i - a_j)$ 不存在。

      因为当 $j$ 递增时,$a_j$ 不增,所以去掉的钩长也互不相同,我们总共会去掉 $n - i$ 个钩长。

      因此式子可以转化为

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

      所以有:

    $$
    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)]
    $$

      现在的问题转化为计算右半部分。

      不难注意到 $(a_i - i) - (a_j - j)$ 不会太大,并且总是正数。

      所以考虑直接计算每种值出现了多少次。

      这个是基础 NTT 操作。

      然后就做完了。

    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] = a - b;
    					}
    				}
    			}
    
    			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
    ", ans.v);
    	return 0;
    }
  • 相关阅读:
    Nop中的Cache浅析
    使用DapperExtensions实现简单仓储
    使用nodejs爬取拉勾苏州和上海的.NET职位信息
    使用Microsoft Fakes进行单元测试(2)
    使用Microsoft Fakes进行单元测试(1)
    小程序之滚动到页面底部
    小程序之动态修改页面标题
    小程序之面试题
    小程序之公共组件的开发
    小程序之web-view打开外部链接
  • 原文地址:https://www.cnblogs.com/yyf0309/p/11320531.html
Copyright © 2020-2023  润新知