zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    LintCode Python 简单级题目 488.快乐数
    LintCode Python 简单级题目 100.删除排序数组中的重复数字 101.删除排序数组中的重复数字II
    LintCode Python 简单级题目 373.奇偶分割数组
    LintCode Python 简单级题目 39.恢复旋转排序数组
    LintCode Python 简单级题目 35.翻转链表
    LintCode Python 简单级题目 451.两两交换链表中的节点
    LintCode Python 简单级题目 174.删除链表中倒数第n个节点
    aws查看官方centos镜像imageid
    linux shell脚本查找重复行/查找非重复行/去除重复行/重复行统计
    php配置优化-生产环境应用版
  • 原文地址:https://www.cnblogs.com/yyf0309/p/11320531.html
Copyright © 2011-2022 走看看