题解 UOJ #102. 【集训队互测2015】ydc的奖金

传送门

调到我心态都快崩了,发现了板子的一堆问题


【分析】

对于第 \(i\) 个人,我们不妨假设它的得分 \(X_i\) 的概率分布函数为 \(F_i(x)=\bold{Pr}(X_i\leq x)\)

假定我们取 \(X_i\) 落入一个极小的区间区间 \([x, x+\Delta x]\) 的概率来代替 \(X_i\) 取得 \(x\) 的概率,则根据题意,得到:

\(\displaystyle \lim_{\Delta x\to 0}{F_i(x+\Delta x)-F_i(x)\over F_i(\Delta x)-F_i(0)}={\bold{Pr}(X_i=x)\over \bold{Pr}(X_i=0)}={f_i(x)\over f_i(0)}\)

于是我们得到:\(\displaystyle {\text d\over \text dx}F_i(x)=k_i\cdot f_i(x)\) ,则 \(\displaystyle F_i(x)=k_i\cdot \int_{-\infty}^x f_i(x)\text dx\)

考虑到随机变量在区间 \([0, 1]\) 内分布,故根据概率的归一性,有 \(\displaystyle F_i(1)=k_i\cdot \int_0^1 f_i(x)\text dx=1\) ,所以 \(\displaystyle k_i=(\int_0^1 f_i(x)\text dx)^{-1}\)

从概率意义上说,\(f_i(x)\) 是随机变量 \(X_i\) 概率密度函数的一个倍数。


我们考虑,若第 \(n\) 人的随机变量取值为 \(x\) 时,对答案的贡献如何计算。

对于第 \(i\) 人,取得的数值比 \(x\) 小的概率为 \(F_i(x)\) ;而更大的为 \(1-F_i(x)\)

那如何统计比 \(x\) 高的数量为 \(k(k\in[0,n-1])\) 的概率呢?

我们考虑构造一个二维生成函数 \(F_i(x)+(1-F_i(x))y\),将他们累乘起来,\(y^k\) 的占位多项式系数即为比 \(x\) 高的数量为 \(k\) 的概率。

而考虑第 \(n\) 人取 \(x\) 的概率同样用 \([x, x+\Delta x]\) 代替,同理取极限得到 \(\displaystyle {1\over k_n}f_n(x)\text dx\)

于是,我们对 \([0, 1]\) 中无限多的 \(x\) 进行积分,得到 \(\displaystyle G(y)=\int_0^1 \prod_{i=1}^{n-1}[F_i(x)+(1-F_i(x))y]\cdot {1\over k_n}f_n(x)\text dx\)

那么,\([y^k]G(y)\) 即为对 \(x\in[0, 1]\) ,有 \(k\) 人比第 \(n\) 人随机变量更大的概率。

而有 \(k\) 人比第 \(n\) 人随机变量更大,即为第 \(n\) 人的排名为第 \(k+1\) 名。

因此,答案为 \(\displaystyle \sum_{i=1}^n p_i\cdot [y^{i-1}]G(y)\)

对于 \(G(y)\) 的求解,关键在于如何求出不带外层积分的函数 \(\displaystyle G(x, y)=\prod_{i=1}^{n-1}[F_i(x)+(1-F_i(x))y]\cdot {1\over k_n}f_n(x)\) 。很显然,这个问题可以由二维 FFT 解决。


【代码】

#include <bits/stdc++.h>
using namespace std;
#define sz(a) (int)a.size()
#define de(a) cout << #a <<" = "<<a<<endl
#define dd(a) cout << #a <<" = "<<a<<" "
#define rsz(a, x) (a.resize(x))
typedef long long ll;

constexpr int P=998244353;
constexpr int LimBit=12, M=1<<LimBit<<1;
inline int kpow(int a, int x, int p=P) { int ans=1; for(;x;x>>=1, a=(ll)a*a%p) if(x&1) ans=(ll)ans*a%p; return ans; }
inline int exgcd(int a, int b, int &x, int &y) {
	static int g;
	return b?(exgcd(b, a%b, y, x), y-=a/b*x, g):(x=1, y=0, g=a);
}
inline int inv(int a, int p=P) {
	static int x, y;
	return exgcd(a, p, x, y)==1?(x<0?x+p:x):-1;
}
namespace Poly {
	const int G=3;
	struct vir {
		int v;
		vir(int v_=0):v(v_>=P?v_-P:v_) {}
		inline vir operator + (const vir &x) const { return vir(v+x.v); }
		inline vir operator - (const vir &x) const { return vir(v+P-x.v); }
		inline vir operator * (const vir &x) const { return vir((ll)v*x.v%P); }

		inline vir operator - () const { return vir(P-v); }
		inline vir operator ! () const { return vir(inv(v)); }
		inline operator int() const { return v; }
	};
	struct poly : public vector<vir> {
		inline friend ostream& operator << (ostream& out, const poly &p) {
			if(!p.empty()) out<<(int)p[0];
			for(int i=1; i<sz(p); ++i) out<<" "<<(int)p[i];
			return out;
		}
	};

	int N, N_, rev[M];
	vir invN, Inv[M], w[2][M];
	inline void init() {
		N_=-1;
		Inv[1]=1;
		for(int i=2; i<M; ++i)
			Inv[i]=-vir(P/i)*Inv[P%i];
	}
	inline void work() {
		if(N_==N) return ;
		N_=N;
		int d=__builtin_ctz(N);
		vir x(kpow(G, (P-1)/N)), y=!x;
		w[0][0]=w[1][0]=1;
		for(int i=1; i<N; ++i) {
			rev[i]=(rev[i>>1]>>1)|((i&1)<<(d-1));
			w[0][i]=x*w[0][i-1], w[1][i]=y*w[1][i-1];
		}
		invN=!vir(N);
	}

	inline void FFT(vir a[M], int f) {
		static auto make = [=](vir w, vir &a, vir &b) { w=w*a; a=b-w; b=b+w; };
		for(int i=0; i<N; ++i) if(i<rev[i]) swap(a[i], a[rev[i]]);
		for(int i=1; i<N; i<<=1)
			for(int j=0, t=N/(i<<1); j<N; j+=i<<1)
				for(int k=0, l=0; k<i; ++k, l+=t)
					make(w[f][l], a[j+k+i], a[j+k]);
		if(f) for(int i=0; i<N; ++i) a[i]=a[i]*invN;
	}

	vir p1[M], p0[M];
	inline void get_mul(poly &a, poly &b, int na, int nb) {
		for(N=1; N<na+nb-1; N<<=1);
		for(int i=0; i<na; ++i) p1[i]=(int)a[i]; for(int i=na; i<N; ++i) p1[i]=0;
		for(int i=0; i<nb; ++i) p0[i]=(int)b[i]; for(int i=nb; i<N; ++i) p0[i]=0;
		work(); FFT(p1, 0); FFT(p0, 0);
		for(int i=0; i<N; ++i) p1[i]=p1[i]*p0[i];
		FFT(p1, 1);
		rsz(a, na+nb-1); for(int i=0; i<sz(a); ++i) a[i]=p1[i];
	}
	inline void get_int(poly &f, poly &g, int C=0) {
		int siz=sz(f);
		rsz(g, siz+1);
		for(int i=siz; i; --i) g[i]=f[i-1]*Inv[i];
		g[0]=C;
	}

	struct mat : public vector<poly> {
		void ex_resize(int n, int m) {
			this->resize(n);
			for(auto &e : *this)
				rsz(e, m);
		}
	};
	vir m0[1024][M], m1[1024][M];
	inline void get_mul_2D(mat &f, mat &g) {
		static int nfx, nfy, ngx, ngy;
		static int Nx, Ny, sx, sy;
		nfx=sz(f); for(int i=nfy=0; i<nfx; ++i) nfy=max(nfy, sz(f[i]));
		ngx=sz(g); for(int i=ngy=0; i<ngx; ++i) ngy=max(ngy, sz(g[i]));
		sx=nfx+ngx-1; sy=nfy+ngy-1;
		for(Nx=1; Nx<sx; Nx<<=1);
		for(Ny=1; Ny<sy; Ny<<=1);

		N=Nx; work();
		for(int j=0; j<nfy; ++j) {
			for(int i=0; i<N; ++i) p1[i]=0;
			for(int i=0; i<nfx; ++i)
				if(sz(f[i])>j)
					p1[i]=f[i][j];
			FFT(p1, 0);
			for(int i=0; i<Nx; ++i) m1[i][j]=p1[i];
		}
		for(int j=nfy; j<Ny; ++j)
			for(int i=0; i<Nx; ++i)
				m1[i][j]=0;
		for(int j=0; j<ngy; ++j) {
			for(int i=0; i<N; ++i) p0[i]=0;
			for(int i=0; i<ngx; ++i)
				if(sz(g[i])>j)
					p0[i]=g[i][j];
			FFT(p0, 0);
			for(int i=0; i<Nx; ++i) m0[i][j]=p0[i];
		}
		for(int j=ngy; j<Ny; ++j)
			for(int i=0; i<Nx; ++i)
				m0[i][j]=0;

		N=Ny; work();
		for(int i=0; i<Nx; ++i) FFT(m1[i], 0);
		for(int i=0; i<Nx; ++i) FFT(m0[i], 0);

		for(int i=0; i<Nx; ++i)
			for(int j=0; j<Ny; ++j)
				m1[i][j]=m1[i][j]*m0[i][j];

		for(int i=0; i<Nx; ++i) FFT(m1[i], 1);
		N=Nx; work();
		f.ex_resize(sx, sy);
		for(int j=0; j<sy; ++j) {
			for(int i=0; i<Nx; ++i) p1[i]=m1[i][j];
			FFT(p1, 1);
			for(int i=0; i<sx; ++i) f[i][j]=p1[i];
		}
		for(auto &p : f) {
			int siz=sz(p);
			while(siz>1&&p[siz-1].v==0)
				--siz;
			rsz(p, siz);
		}
	}
}
using Poly::mat;
using Poly::poly;
using Poly::vir;
using Poly::Inv;

inline vir get_value(const poly &p) {
	vir x;
	for(int i=0; i<sz(p); ++i)
		x=x+p[i]*Inv[i+1];
	return x;
}

constexpr int MAXN=512;
mat f[MAXN];
poly tmp;
vir p[MAXN], prod;
int n;
void merge(int l, int r) {
	if(l==r)
		return ;
	int mid=l+r>>1;
	merge(l, mid);
	merge(mid+1, r);
	Poly::get_mul_2D(f[r], f[mid]);
}
inline void work() {
	merge(1, n);
	vir x;
	for(int i=sz(f[n])-1; i>=0; --i)
		x=x+get_value(f[n][i])*p[i];
	cout<<(int)(x*!prod);
}
inline void init() {
	Poly::init();
	cin>>n;
	for(int i=0, v; i<n; ++i)
		cin>>v, p[i]=v;
	
	int t;
	vir x;
	prod=1;
	for(int i=1; i<n; ++i) {
		cin>>t;
		rsz(tmp, t);
		for(int j=0, v; j<t; ++j)
			cin>>v, tmp[j]=v;
		x=get_value(tmp);
		prod=prod*x;
		Poly::get_int(tmp, tmp);
		rsz(f[i], 2);
		f[i][0]=tmp;
		for(auto &e : tmp)
			e=-e;
		tmp[0]=tmp[0]+x;
		f[i][1]=tmp;
	}
	cin>>t;
	rsz(tmp, t);
	for(int i=0, v; i<t; ++i)
		cin>>v, tmp[i]=v;
	prod=prod*get_value(tmp);
	rsz(f[n], 1);
	f[n][0]=tmp;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	init();
	work();
	cout.flush();
	return 0;
}

目前 rank1 ,领先第二名近 700 ms 。

posted @ 2022-06-03 16:38  JustinRochester  阅读(42)  评论(0编辑  收藏  举报