線性代數,概率與期望題單

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. 無意識之外的捉迷藏

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