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

Beautiful Bracket Sequence (hard version)

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

发现一个性质这题就很好做了。
对于一个只有()()的字符串,一定只有一个点xx,满足它左边1...x1...x的左括号数等于x+1...nx+1...n的右括号数,并且这个字符串的深度就是它左边的左括号数。
那么我们枚举每个位置xx,假设左边有aa个左括号,有cc个问号,右边有yy个右括号,zz个问号。
则答案为i=max(a,y)min(a+c,y+z)i(cia)(ziy)\sum_{i=\max(a,y)}^{\min(a+c,y+z)}i\binom{c}{i-a}\binom z{i-y}
发现因为组合数下标为0或者大于上标都为00,可以把式子写成枚举范围易于范德蒙德卷积的形式:
ii(cia)(zz+yi)=(ia)(cia)(zz+yi)+a(cia)(zz+yi)\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}
=c(c1ia1)(zz+yi)+a(c+zz+ya)=\sum c\binom {c-1}{i-a-1}\binom z{z+y-i} + a \binom {c+z}{z+y-a}
=c(c+z1z+ya1)+a(c+zz+ya)= c\binom {c+z-1}{z+y-a-1} + a\binom {c+z}{z+y-a}
AC Code\mathcal AC \ Code

#include<bits/stdc++.h>
#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

数轴上有nn个粒子,每个有viv_i的初速度,有pip_i的机率往左运动(有1pi1-p_i距离往右),求第一次碰撞发生的时间的期望值,如果没有碰撞则为00

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

a0/1,0/1a_{0/1,0/1}表示前为0/10/1,后为0/10/1可行的概率。
[fu,0,fu,1]=[fv,0,fv,1]×[a0,0a0,1a1,0a1,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}

zkwzkw线段树实现了一发,注意区间积超过nn的位置也要赋初值。
AC Code\mathcal AC \ Code

#include<bits/stdc++.h>
#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

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

#include<bits/stdc++.h>
#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,kn,k求对长度为nn的随机排列,做归并排序,但是只递归kk层,求逆序对的期望个数。

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

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

AC Code\mathcal AC \ Code

#include<bits/stdc++.h>
#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」龙与地下城

YY个概率均匀取值在0...X10...X-1的离散变量{xi}\{x_{i}\}
AxiBA\leq \sum x_i \leq B的概率。

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

AC Code\mathcal AC \ Code

#include<bits/stdc++.h>
#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

CodeCode

#include<bits/stdc++.h>
#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<int>
#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. 无意识之外的捉迷藏

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章