• LOJ#6051. 「雅礼集训 2017 Day11」PATH 题解


    注意:本题解数组下标从 (1) 开始

    题目链接

    不难发现概率为可行方案数/总方案数。

    总方案数显然为 (large frac {(sumlimits_{i=1}^{n} a_i)!}{prodlimits_{i=1}^{n} a_i!})

    可行方案数如何计算?

    考虑记 (t_{i,j}) 表示第 (i)(x_i) 的值刚好加到 (j) 的时间。

    不难发现只有 (i leq n)(j leq a_i) 的时候, (t_{i,j}) 才有意义, 并且 (t_{i,j} < t_{i,j+1})(如果存在) , (t_{i,j} < t_{i+1,j})(如果存在)

    那么这就是一个给定形状的杨表计数问题。

    杨表计数问题的答案为 (large frac {(sumlimits_{i=1}^{n} a_i)!}{ sumlimits_{ileq n,j leq a_i} Cnt(i,j)}) , 其中 (Cnt(i,j))((i,j)) 这个格子的钩长+1.

    (b_x = sumlimits_{i=1}^{x} [a_i leq x]) , 不难发现如果 ((i,j)) 在杨表内部,其 (Cnt(i,j) = (a_i - j) + (b_j - i) + 1) ,否则 ((a_i - j) + (b_j - i) + 1) 会小于 (0) .

    那么直接把式子拆成 ((a_i - i) + (b_j - j) + 1) 即可,可以直接 (NTT) 或者 (FFT) 解决,最后乘上 (prodlimits_{i=1}^{n} a_i!) , 而 ((sumlimits_{i=1}^{n} a_i)!) 这个巨大的阶乘在两个方案数相除的时候已经被消掉了,不需要处理。

    (Theta(Nlog N)) , 其中 (N)((n+maxlimits_{i=1}^{n} a_i))

    code :

    #include <bits/stdc++.h>
    #define LL unsigned long long
    #define uint unsigned
    using namespace std;
    template <typename T> void read(T &x){
    	static char ch; x = 0;
    	while (!isdigit(ch)) ch = getchar();
    	while (isdigit(ch)) x = x * 10 + ch - '0',ch = getchar();
    }
    inline void write(uint x){if (x > 9) write(x/10); putchar(x%10+'0'); }
    
    const uint P = 1004535809,N = 500005,L = 2097152;
    inline uint power(uint x,int y){
    	static uint r; r = 1; while (y){ if (y&1) r = (LL)r * x % P; x = (LL)x * x % P,y >>= 1; } return r;
    }
    
    uint R[L],F[L],G[L],wn[L<<1],iwn[L<<1];
    inline int getR(int n){
    	int Lim = 2,l = 1; while (Lim <= n) Lim <<= 1,++l; 
    	for (register int i = 1; i < Lim; ++i) R[i] = (R[i>>1]>>1) | ((i&1)<<l-1);
    	return Lim;
    }
    inline void NTT(uint *A,int n){
    	register int i,j,k,v;
    	for (i = 1; i < n; ++i) if (i < R[i]) swap(A[i],A[R[i]]); 
    	for (i = 1; i < n; i <<= 1) for (j = 0; j < n; j += i << 1) for (k = j; k < i+j; ++k)
    		v = (LL)wn[(i<<1)+k-j] * A[k+i] % P,A[k+i] = (A[k]<v)?(A[k]+P-v):(A[k]-v),A[k] = (A[k]+v>=P)?(A[k]+v-P):(A[k]+v);
    }
    inline void iNTT(uint *A,int n){
    	register int i,j,k,v;
    	for (i = 1; i < n; ++i) if (i < R[i]) swap(A[i],A[R[i]]); 
    	for (i = 1; i < n; i <<= 1) for (j = 0; j < n; j += i << 1) for (k = j; k < i+j; ++k)
    		v = (LL)iwn[(i<<1)+k-j] * A[k+i] % P,A[k+i] = (A[k]<v)?(A[k]+P-v):(A[k]-v),A[k] = (A[k]+v>=P)?(A[k]+v-P):(A[k]+v);
    	for (i = 0,v = power(n,P-2); i < n; ++i) A[i] = (LL)A[i] * v % P; 
    }
    int n,m,a[N],b[N],fac[N];
    int main(){
    	int i,j,L; uint v;
    	read(n);
    	for (i = 1; i <= n; ++i) read(a[i]),m = max(a[i],m),++b[a[i]],++F[a[i]+n-i];
    	for (i = m; i ; --i) b[i] += b[i+1],++G[b[i]+m+1-i];
    	L = getR((n+m<<1)+1);
    	for (i = 1,j = 2; i <= 21 && (1<<i) <= L; ++i,j <<= 1){
    		int l = j + (j>>1);
    		v = power(3,(P-1)/j),wn[j] = 1;
    		for (register int k = j+1; k < l; ++k) wn[k] = (LL)wn[k-1] * v % P;
    		v = power(v,P-2),iwn[j] = 1;
    		for (register int k = j+1; k < l; ++k) iwn[k] = (LL)iwn[k-1] * v % P;
    	}
    	NTT(F,L),NTT(G,L); for (i = 0; i < L; ++i) F[i] = (LL)F[i] * G[i] % P; iNTT(F,L);
    	for (v = 1,i = n+m+1; i < L; ++i) if (F[i]) v = (LL)v * power(i-n-m,F[i]) % P;
    	v = power(v,P-2);
    	for (fac[0] = i = 1; i <= m; ++i) fac[i] = (LL)fac[i-1] * i % P;
    	for (i = 1; i <= n; ++i) v = (LL)v * fac[a[i]] % P; 
    	write(v),putchar('
    ');
    	return 0;
    }
    
  • 相关阅读:
    ASP.NET 2.0中使用webpart系列控件
    ASP.NET AJAX入门系列(11):在多个UpdatePanle中使用Timer控件(转)
    WebService SoapHeader的使用
    (转)对程序员职业的一些建议
    android组件间通信
    关于android library project
    (转)我6个月的学习编程经历:从”大齿怪“到“狂欢者”
    DOM的一个要注意的地方
    android的休眠对service,activity的影响
    java的字符串和char数组
  • 原文地址:https://www.cnblogs.com/s-r-f/p/13727036.html
Copyright © 2020-2023  润新知