线性代数,概率与期望题单

Beautiful Bracket Sequence (hard version)

题意:给出一个包含 ( ) ? ()? ()?的字符串,求将 ? ? ?变为 ( ) () ()之一的所有方案中,字符串的深度和。深度 d d d为其子序列中最长的为 2 ∗ d 2*d 2d的字符串其中前 d d d位都是 ( ( ( d d d位都是 ) ) )

发现一个性质这题就很好做了。
对于一个只有 ( ) () ()的字符串,一定只有一个点 x x x,满足它左边 1... x 1...x 1...x的左括号数等于 x + 1... n x+1...n x+1...n的右括号数,并且这个字符串的深度就是它左边的左括号数。
那么我们枚举每个位置 x x x,假设左边有 a a a个左括号,有 c c c个问号,右边有 y y y个右括号, z z z个问号。
则答案为 ∑ i = max ⁡ ( a , y ) min ⁡ ( a + c , y + z ) i ( c i − a ) ( z i − y ) \sum_{i=\max(a,y)}^{\min(a+c,y+z)}i\binom{c}{i-a}\binom z{i-y} i=max(a,y)min(a+c,y+z)i(iac)(iyz)
发现因为组合数下标为0或者大于上标都为 0 0 0,可以把式子写成枚举范围易于范德蒙德卷积的形式:
∑ i i ( c i − a ) ( z z + y − i ) = ∑ ( i − a ) ( c i − a ) ( z z + y − i ) + a ∑ ( c i − a ) ( z z + y − i ) \sum_i i \binom c{i-a} \binom z{z+y-i} = \sum (i-a)\binom c{i-a} \binom z{z+y-i} + a\sum \binom c{i-a} \binom z{z+y-i} ii(iac)(z+yiz)=(ia)(iac)(z+yiz)+a(iac)(z+yiz)
= ∑ c ( c − 1 i − a − 1 ) ( z z + y − i ) + a ( c + z z + y − a ) =\sum c\binom {c-1}{i-a-1}\binom z{z+y-i} + a \binom {c+z}{z+y-a} =c(ia1c1)(z+yiz)+a(z+yac+z)
= c ( c + z − 1 z + y − a − 1 ) + a ( c + z z + y − a ) = c\binom {c+z-1}{z+y-a-1} + a\binom {c+z}{z+y-a} =c(z+ya1c+z1)+a(z+yac+z)
A C   C o d e \mathcal AC \ Code AC Code

#include
#define maxn 1000006
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define mod 998244353
using namespace std;

int n,fac[maxn],inv[maxn],invf[maxn];
char s[maxn];

int C(int a,int b){ if(a<0||b<0||a<b) return 0;return fac[a] * 1ll * invf[b] % mod * invf[a-b] % mod; }

int main(){
	scanf("%s",s);
	n=strlen(s);
	fac[0]=fac[1]=inv[0]=inv[1]=invf[0]=invf[1]=1;
	rep(i,2,n) fac[i] = 1ll * fac[i-1] * i % mod , inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod,
		invf[i] = 1ll * invf[i-1] * inv[i] % mod;
	int a=0,c=0,y=0,z=0;
	rep(i,0,n-1){
		if(s[i] == ')') y++;
		if(s[i] == '?') z++;
	}
	int ans = 0;
	rep(i,0,n-1){
		if(s[i] == '(') a ++;
		else if(s[i] == ')') y --;
		else c ++ , z -- ;
		ans = (ans + 1ll * a * C(z+c,z+y-a) + 1ll * c * C(z+c-1,z+y-a-1)) % mod;
	}
	printf("%d\n",(ans+mod)%mod);
}

CF1286D LCC

数轴上有 n n n个粒子,每个有 v i v_i vi的初速度,有 p i p_i pi的几率往左运动(有 1 − p i 1-p_i 1pi距离往右),求第一次碰撞发生的时间的期望值,如果没有碰撞则为 0 0 0

发现第一次碰撞一定发生在相邻的点间。
把相邻的点间可能的 3 3 3种碰撞形式都求出来,按照碰撞时间排序,那么就相当于每次禁用一种碰撞然后求概率,发现这个求概率是一个 d p dp dp f u , 0 / 1 f_{u,0/1} fu,0/1表示 u u u是往左还是往右的概率。
可以写矩阵乘积动态维护 d p dp dp

a 0 / 1 , 0 / 1 a_{0/1,0/1} a0/1,0/1表示前为 0 / 1 0/1 0/1,后为 0 / 1 0/1 0/1可行的概率。
[ f u , 0 , f u , 1 ] = [ f v , 0 , f v , 1 ] × [ a 0 , 0 a 0 , 1 a 1 , 0 a 1 , 1 ] [f_{u,0},f_{u,1}] = [f_{v,0},f_{v,1}] \times \begin{bmatrix} a_{0,0} & a_{0,1} \\ a_{1,0} & a_{1,1} \end{bmatrix} [fu,0,fu,1]=[fv,0,fv,1]×[a0,0a1,0a0,1a1,1]

z k w zkw zkw线段树实现了一发,注意区间积超过 n n n的位置也要赋初值。
A C   C o d e \mathcal AC \ Code AC Code

#include
#define maxn 400005
#define db double
#define mod 998244353
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long 
#define Ct const
using namespace std;

int N;
int n,p[maxn],c[maxn],sx[maxn],ty[maxn],px[maxn],py[maxn],cnt,rtm[maxn];
int x[maxn],v[maxn];
db TM[maxn];
bool cmp(Ct int &u,Ct int &v){ return TM[u] < TM[v]; }
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1)r=1ll*r*b%mod;return r; }

struct mat{
	int a[2][2];
	mat (int d=0){memset(a,0,sizeof a);rep(i,0,1) a[i][i]=d;}
	mat operator *(Ct mat &B)Ct{ 
		mat r;
		r.a[0][0] = (1ll * a[0][0] * B.a[0][0] + 1ll * a[0][1] * B.a[1][0]) % mod;
		r.a[0][1] = (1ll * a[0][0] * B.a[0][1] + 1ll * a[0][1] * B.a[1][1]) % mod;
		r.a[1][0] = (1ll * a[1][0] * B.a[0][0] + 1ll * a[1][1] * B.a[1][0]) % mod;
		r.a[1][1] = (1ll * a[1][0] * B.a[0][1] + 1ll * a[1][1] * B.a[1][1]) % mod;
		return r;
	}
}tr[maxn];


int main(){
	scanf("%d",&n);
	int iv = Pow(100 , mod-2);
	rep(i,1,n) scanf("%d%d%d",&x[i],&v[i],&p[i]),p[i]=1ll*p[i]*iv%mod;
	for(N=1;N<n;N<<=1);
	rep(i,1,N)
		rep(j,0,1) rep(k,0,1)
			tr[N+i-1].a[j][k] =  (k ? p[i] : 1 - p[i]);
	per(i,N-1,1) tr[i] = tr[i<<1] * tr[i<<1|1]; 
	rep(i,2,n) rep(j,0,1) rep(k,0,1){
		int vx = j ? v[i-1] : -v[i-1] , vy = k ? v[i] : -v[i];
		if(vx <= vy) continue;
		sx[++cnt] = i-1 , ty[cnt] = i;
		px[cnt] = j , py[cnt] = k;
		TM[cnt] = 1.0 * (x[i] - x[i-1]) / (vx - vy);
		rtm[cnt] = (x[i] - x[i-1]) * 1ll * Pow(vx - vy , mod-2) % mod;
		c[cnt] = cnt;
	}
	sort(c+1,c+1+cnt,cmp);
	int ans = 0;
	rep(i,1,cnt){
		int u = c[i];
		mat tmp = tr[ty[u] + N - 1];
		tr[ty[u] + N - 1] = mat();
		tr[ty[u] + N - 1].a[px[u]][py[u]] = tmp.a[px[u]][py[u]];
		for(int p=(ty[u]+N-1)>>1;p;p>>=1) tr[p] = tr[p<<1] * tr[p<<1|1];
		ans = (ans + 1ll * rtm[u] * (tr[1].a[0][0] + tr[1].a[0][1])) % mod;
		tmp.a[px[u]][py[u]] = 0;
		tr[ty[u] + N - 1] = tmp;
		for(int p=(ty[u]+N-1)>>1;p;p>>=1) tr[p] = tr[p<<1] * tr[p<<1|1];
	}
	printf("%d\n",(ans+mod)%mod);
}

CF1267G Game Relics

线性代数,概率与期望题单_第1张图片
题解:
这个题的随机购买操作是在所有的物品中随机,所以可以发现随机操作一定是越来越不优秀的。
而且因为给出了 x ≤ c x \leq c xc,也就是说第一步随机一定最优。
可以猜想:一定是先一直随机,到某个时刻后就开始购买所有的物品。
具体证明,咕咕咕了因为随机操作一定是越来越不优秀的,一旦某个时刻选择购买,后面也肯定选择购买。
但是我们不太好处理随机到已经拥有了哪些物品再开始购买。
所以我们把购买看做是在未拥有的物品中随机一个买。
那么实际上就是每个时刻有两种随机策略,选期望值小的一方,
最后总花费的期望值就是 = = =每个时刻购买花费 × \times ×到达这个时刻的概率。
A C   C o d e \mathcal AC \ Code AC Code

#include
#define maxn 105
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define db double
using namespace std;

int n,c[maxn];
db f[maxn][10005],C[maxn][maxn],x;

int main(){
	scanf("%d%lf",&n,&x);
	int sm = 0;
	f[0][0] = 1;
	rep(i,1,n){
		scanf("%d",&c[i]);	
		sm += c[i];
		per(j,i,1) per(k,sm,c[i]) 
			f[j][k] = (f[j][k] + f[j-1][k-c[i]]);
	}
	db ans = 0;
	rep(i,C[0][0]=1,n) rep(j,C[i][0]=1,i)
		C[i][j] = C[i-1][j-1] + C[i-1][j];
	rep(i,0,n-1) rep(k,0,sm)
		ans += f[i][k] / C[n][i] * min((sm-k) * 1.0 / (n-i) , (1.0 * n / (n-i) + 1) * x / 2);
	printf("%.10lf\n",ans);
}

CF1081G Mergesort Strikes Back

给出 n , k n,k n,k求对长度为 n n n的随机排列,做归并排序,但是只递归 k k k层,求逆序对的期望个数。

题解:递归 k k k层后的区间因为不会排序直接就是随机区间,假设其长度为 l l l,那么期望逆序对个数就是 l ( l − 1 ) 4 \frac {l(l-1)}4 4l(l1)
考虑对无序的两个序列做归并会发生什么。
如果我们是要从小到大的顺序。
发现实际上在某个序列是前一个大于后一个,那么前一个被选出来之后后一个马上也被选。
实际上是一个被选出来之后后面小于等于它的都会被依次选出来。
所以就是以前缀最大值来将无序的序列分成若干块,归并前后块头都是有序的,块头以外的数就像是块头的附赠品一样顺序完全不会变。

所以对于两个不在同一个区间的数,他们的块头分别是他们的前缀最大值,
发现如果这两个前缀最大值中较大的那个也就是他们前面的所有数中最大的那个是这两个数之一,就完全不可能构成逆序对。
否则这两个数块头不知道谁大,数也不知道谁大,贡献逆序对的概率就是 1 2 \frac 12 21
所以如果第一个数前有 i i i个数,第二个数前有 j j j个数,他们之间是逆序对的期望就是 i + j − 2 2 ( i + j ) = 1 2 − 1 i + j \frac {i+j-2}{2(i+j)} = \frac 12 - \frac 1{i+j} 2(i+j)i+j2=21i+j1
一个长度为 x x x的区间和长度为 y y y的区间的贡献就是
x y 2 − ∑ i = 1 x ∑ j = 1 y 1 x + y \frac {xy}2 - \sum_{i=1}^x \sum_{j=1}^y \frac {1}{x+y} 2xyi=1xj=1yx+y1
这个题因为底层长度只有两种所以怎么做都能过。

A C   C o d e \mathcal AC \ Code AC Code

#include
#define fi first
#define se second
#define mkp make_pair
#define pb push_back
using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N=1e5+10;
int n,k,mod,ans;
int inv[N],sum[N];
map<int,int>cnt;
map<int,int>::iterator it1,it2;

int read()
{
	int ret=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=0;c=getchar();}
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return f?ret:-ret;
}

void up(int &x,int y){x+=y;if(x>=mod)x-=mod;if(x<0)x+=mod;}
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1) r=1ll*r*b%mod; return r; }
void divide(int l,int r,int dp){
	if(dp<=1 || l==r){cnt[r-l+1]++;return;}
	int mid=(l+r)>>1;
	divide(l,mid,dp-1);divide(mid+1,r,dp-1);
}
int calc(int x,int y){
	int res=(ll)x*y%mod;
	for(int i=1;i<=x;++i) up(res,-(sum[i+y]-sum[i])*2%mod);
	return res;
}

int main(){
	n=read();k=read();mod=read();
	for(int i=1;i<N;++i) sum[i]=inv[i]=Pow(i,mod-2),up(sum[i],sum[i-1]);
	divide(1,n,k);
	for(it1=cnt.begin();it1!=cnt.end();++it1)
	{
		int t=it1->fi,s=it1->se;
		up(ans,(ll)t*(t-1)%mod*inv[2]%mod*s%mod);
		up(ans,(ll)s*(s-1)%mod*inv[2]%mod*calc(t,t)%mod);
	}
	for(it1=cnt.begin();it1!=cnt.end();++it1)
		for(it2=cnt.begin();it2!=cnt.end();++it2) 
		{
			int x=it1->fi,y=it2->fi;
			if(x>=y) continue;
			up(ans,(ll)calc(x,y)*it1->se%mod*it2->se%mod); 
		}
	printf("%d\n",(ll)ans*inv[2]%mod);
}
 

「SDOI2017」龙与地下城

Y Y Y个概率均匀取值在 0... X − 1 0...X-1 0...X1的离散变量 { x i } \{x_{i}\} {xi}
A ≤ ∑ x i ≤ B A\leq \sum x_i \leq B AxiB的概率。

容易发现当 Y Y Y很大的时候这个概率我们可以抽象成正态分布。
出题人给出了其期望为 μ = n X − 1 2 \mu = n\frac {X-1}2 μ=n2X1,方差为 σ 2 = n X 2 − 1 12 \sigma^2 = n\frac {X^2-1}{12} σ2=n12X21
等等为什么 n n n个变量和的方差只需要 × n \times n ×n
线性代数,概率与期望题单_第2张图片
为了方便调用库函数,我们将这个正态分布化为 N ( 0 , 1 ) N(0,1) N(0,1)也即标准正态分布。
具体的,我们就是要求 N ( 0 , 1 ) N(0,1) N(0,1) A − μ σ → B − μ σ \frac {A - \mu}{\sigma} \rightarrow \frac {B - \mu}{\sigma} σAμσBμ的和。
N ( 0 , 1 ) N(0,1) N(0,1)的概率分布函数: 1 2 π ∫ 0 x e − t 2 2 d t \frac 1{\sqrt {2\pi}}\int_0^xe^{\frac {-t^2}2}{\rm d}t 2π 10xe2t2dt
换一下元 u = t 2 u = \frac t{\sqrt 2} u=2 t
则原式变为 1 2 π 2 ∫ 0 x 2 e − u 2 d u \frac 1{\sqrt {2\pi}}\sqrt 2\int_0^{\frac x{\sqrt 2}}e^{-u^2}{\rm d}u 2π 12 02 xeu2du
(因为 ∫ \int 的范围缩小了 2 \sqrt 2 2 倍所以要乘上 2 \sqrt 2 2 。)
线性代数,概率与期望题单_第3张图片
所以原式为 1 2 e r f ( x 2 ) \frac 12 {\rm erf(\frac x{\sqrt 2})} 21erf(2 x),大范围直接计算。
对于小范围的数据不一定是很符合正态分布的,
但是我们发现小范围就直接是一个多项式快速幂,
发现这个底数多项式的次数才 20 20 20
这放在2020年还写FFT就有点问题了
直接 O ( 20 Y ) O(20Y) O(20Y)求多项式快速幂即可。

A C   C o d e \mathcal AC \ Code AC Code

#include
#define db double
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

db X,Y,mu,sig;
#define maxn 2500
db A[maxn],B[maxn],sm[maxn];

int main(){
	int T;
	for(scanf("%d",&T);T--;){
		scanf("%lf%lf",&X,&Y);
		if(X * Y <= 2000){
			B[0] = pow(1/X,Y);
			rep(i,1,X*Y-1){
				B[i] = 0;
				rep(j,1,min((int)X-1,i))
					B[i] += Y * j * B[i-j] - (i-j) * B[i-j];
				B[i] /= i;
			}
			rep(i,0,X*Y-1) sm[i] = (i ? sm[i-1] : 0) + B[i];
			for(int tim=1;tim<=10;tim++){
				int a,b;scanf("%d%d",&a,&b);
				printf("%.10lf\n",sm[b] - (a ? sm[a-1] : 0));
			}
		}
		else{
			mu = (X-1) / 2 * Y , sig = sqrt((X*X-1) / 12 * Y);
			for(int tim = 1;tim <= 10;tim++){
				int A,B;
				scanf("%d%d",&A,&B);
				printf("%.10lf\n",(erf((B - mu) / sig / sqrt(2)) - erf((A - 1 - mu) / sig / sqrt(2))) / 2);
			} 
		}
	}
}

CodeForces - 506E Mr. Kitayuta’s Gift

C o d e Code Code

#include
#define maxn 205
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define vi vector
#define pb push_back
#define mod 10007
#define Ct const
using namespace std;

int f[maxn * 10][maxn][maxn];
char s[maxn];
int n;
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1)r=1ll*r*b%mod;return r; }
vi BM(vi &a){
	vi r(1,1),p,t;int l;
	rep(i,0,a.size()-1){
		int b=0;
		rep(j,0,r.size()-1) b = (b + 1ll * r[j] * a[i-j]) % mod;
		if(!b) continue;
		t = r , r.resize(max(r.size() , i+2 - (r.size() - (r.size() != 0))));
		rep(j,0,p.size()-1) r[i-l+j] = (r[i-l+j] - 1ll * p[j] * b) % mod;
		p = t;int iv = Pow(b , mod-2);
		rep(j,0,p.size()-1) p[j] = 1ll * p[j] * iv % mod;
		l = i;
	}
	return r;
}
typedef vi poly;
poly Mul(const poly &A,const poly &B,const poly &P){
	poly r(A.size() + B.size() - 1);
	rep(i,0,A.size()-1) rep(j,0,B.size()-1) r[i+j] = (r[i+j] + 1ll * A[i] * B[j]) % mod;
	per(i,r.size()-1,P.size()-1) if(r[i]){// in this problem P[P.size()-1] = 1 , so there is no need to getinv.
		int t = r[i];
		rep(j,1,P.size())
			r[i-j+1] = (r[i-j+1] - 1ll * P[P.size()-j] * t) % mod;
	}
	r.resize(P.size()-1);
	return r;
}

int main(){
	scanf("%s",s+1);
	n = strlen(s+1);
	
	f[0][0][n+1] = 1;
	rep(i,0,7*n) rep(j,0,n) per(k,n+1,j+1) if(f[i][j][k]){
		if(s[j+1] == s[k-1]){
			if(j + 1 <= k - 1){
				f[i][j+1][k-1] = (f[i][j+1][k-1] + f[i][j][k]) % mod;
				f[i+2][j][k] = (f[i+2][j][k] + 25ll * f[i][j][k]) % mod;
			}
			else 
				f[i+2][j][k] = (f[i+2][j][k] + 26ll * f[i][j][k]) % mod;
			if(j + 1 == k - 1)
				f[i+1][j][k-1] = (f[i+1][j][k-1] + f[i][j][k]) % mod;
		}
		else{
			if(j + 1 <= k - 1){
				f[i+2][j][k] = (f[i+2][j][k] + 24ll * f[i][j][k]) % mod;
				f[i+1][j+1][k] = (f[i+1][j+1][k] + f[i][j][k]) % mod;
				f[i+1][j][k-1] = (f[i+1][j][k-1] + f[i][j][k]) % mod;
			}
			else 
				f[i+2][j][k] = (f[i+2][j][k] + 26ll * f[i][j][k]) % mod;
		}
	}
	vi a;
	rep(i,0,7*n){
		int sm = 0;
		rep(j,1,n) sm = (sm + f[i][j][j]) % mod;
		rep(j,0,n) sm = (sm + f[i][j][j+1]) % mod;
		if(i) rep(j,0,n) sm  = (sm + f[i-1][j][j+1] * 26ll) % mod;
		a.push_back(sm);
	}
	vi P = BM(a);
	reverse(P.begin(),P.end());
	poly r(1,1),t(2,0);
	t[1] = 1;
	int m;scanf("%d",&m);
	for(;m;m>>=1,t=Mul(t,t,P)) if(m&1)
		r = Mul(r,t,P);
	int ans = 0;
	rep(i,0,r.size()-1) ans = (ans + 1ll * r[i] * a[i]) % mod;
	printf("%d\n",(ans+mod)%mod);
} 

LOJ #6295. 无意识之外的捉迷藏

你可能感兴趣的:(线性代数,概率与期望题单)