首页 > 其他分享 >2023.1.17日寄

2023.1.17日寄

时间:2023-01-18 21:33:53浏览次数:36  
标签:ch frac 17 int sum fa 2023.1 dp

\(~~~~\) 怎么当天写好的东西又忘了发了/fn

一言

让死亡觊觎我
让恐惧亲吻我
来摧毁我深爱的一切
可仍夺不走我的选择
弹指间湮灭我
但命运打不败活着
让生命如剧烈的烟火
璀璨熄灭前也将点亮
孩童的双眸
——————《人是_》

今日复习内容:概率期望

「PKUWC2018」 随机游走

题意

\(~~~~\) 从 \(n\) 个点的树上给定的 \(x\) 点开始随机游走,每次询问至少走到 \(S\) 集合内所有的点一次的期望步数。
\(~~~~\) \(1\leq n\leq 18,1\leq Q\leq 5000\).

题解

\(~~~~\) 虽然这个转化过的题意看起来就很min-max容斥,但我们不用。

\(~~~~\) 套路性地设 \(dp_{S,i}\) 为 \(S\) 集合内已经被遍历过,当前在 \(x\) 点的答案,但注意到有环转移。

\(~~~~\) 为此我们把转移分为两部分,集合内和集合外的,在做dp的时候记录下 \(u\) 的父结点 \(fa\),那么就维护 \(A_u,B_u\) 使得:

\[dp_u=A_u \times dp_{fa}+B_u \]

\(~~~~\) ( \(dp\) 都是在 \(S\) 意义下的,所以省略掉了 \(S\) 维,下文非 \(S\) 维)

\(~~~~\) 对于叶子结点,都有 \(A_u=B_u=1\).

\(~~~~\) 否则对于 \(\pmb{u \not \in S}\),有:

\[dp_u=\frac{1}{deg_u}\sum_{v \text{~near~} u}(f_{v}+1)\\ =\frac{1}{deg_u}dp_{fa_u}+\frac{1}{deg_u}\sum_{u=fa_v}(A_v\times dp_u+B_v+1)\\ =\frac{1}{deg_u-\sum_{u=fa_v}A_v}dp_{fa_u}+\frac{\sum_{u=fa_v}(B_v+1)+1}{deg_u-\sum_{u=fa_v}A_v} \]

\(~~~~\) 由此可以推出 \(A_u\) 和 \(B_u\):

\[A_u=\frac{1}{deg_u-\sum_{u=fa_v}A_v}\\B_u=\frac{\sum_{u=fa_v}(B_v+1)+1}{deg_u-\sum_{u=fa_v}A_v} \]

\(~~~~\) 直接推出来就好了

\(~~~~\) 而对于 \(\pmb{u \in S}\):

\(~~~~\) 若 \(\{u\}=S\) ,直接取 \(A_u=B_u=0\).

\(~~~~\) 否则:\(A_u=0,B_u=\frac{1}{deg_u}\sum_{v~\text{near}~u}(f_{S+\{u\},v}+1)\)

\(~~~~\) 总之就是不存在环状更新所以随便推推就好了.

\(~~~~\) 最后把每个 \(dp\) 直接用 \(A,B\) 还原就好了,答案就每次直接得到集合后直接查dp数组就好了。

代码
查看代码
#include <bits/stdc++.h>
using namespace std;
template<typename T>void read(T &x)
{
    T f=1;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    x*=f;
}
int deg[20];
vector <int> G[20];
const int MOD=998244353;
inline int Add(int x,int y){int z=x+y;return z>=MOD?z-MOD:z;}
inline int Dec(int x,int y){int z=x-y;return z<0?z+MOD:z;}
inline int Mul(int x,int y){return 1ll*x*y%MOD;}
inline int qpow(int a,int b)
{
	int ret=1;
	while(b)
	{
		if(b&1) ret=Mul(ret,a);
		b>>=1;a=Mul(a,a);
	}
	return ret;
}
int A[20],B[20];
int dp[262145][20];
void dfs1(int S,int u,int fa)
{
	int SA=0,SB=0;A[u]=B[u]=0;
	for(int i=0;i<(int)G[u].size();i++)
	{
		int v=G[u][i];
		if(v==fa) continue;
		dfs1(S,v,u);
		if(!(S&(1<<(v-1)))) B[u]=Add(B[u],dp[S|(1<<(v-1))][v]);
		else SA=Add(SA,A[v]),SB=Add(SB,B[v]);
	}
	A[u]=qpow(Dec(deg[u],SA),MOD-2);
	B[u]=Mul(A[u],Add(deg[u],Add(B[u],SB)));
	if(!fa) {A[u]=0;return;}
	if(!(S&(1<<(fa-1)))) B[u]=Add(B[u],Mul(A[u],dp[S|(1<<(fa-1))][fa])),A[u]=0;
}
void dfs2(int S,int u,int fa)
{
	dp[S][u]=Add(Mul(A[u],dp[S][fa]),B[u]);
	for(int i=0;i<(int)G[u].size();i++) if(G[u][i]!=fa) dfs2(S,G[u][i],u);
}
int main() {
	int n,Q,x;read(n);read(Q);read(x);
	for(int i=1,u,v;i<n;i++)
	{
		read(u),read(v);
		deg[u]++; deg[v]++;
		G[u].push_back(v); G[v].push_back(u);
	}
	for(int S=(1<<n)-2;S>=1;S--) dfs1(S,1,0),dfs2(S,1,0);
	while(Q--)
	{
		int k,p,S=(1<<n)-1;
		read(k);
		for(int i=1;i<=k;i++) read(p),S^=(1<<(p-1));
		S|=(1<<(x-1));
		printf("%d\n",dp[S][x]);
	}
	return 0;
}

「ZJOI2015」地震后的幻想乡

题意

\(~~~~\) 给出 \(n\) 个点的无向图 \(G\),边权等概率为 \([0,1]\) 间的实数,求这个图的最小生成树的最大边权期望。
\(~~~~\) 对于 \(n\) 个取值 \([0,1]\) 的随机变量,其第 \(k\) 小值的期望为 \(\frac{k}{n+1}\).
\(~~~~\) \(1\leq n\leq 10,1\leq m\leq \frac{n(n-1)}{2}\).

题解

\(~~~~\) 不想细写这道题,所以随便写写。

\(~~~~\) 根据提示我们不难想用了 \(i\) 条边联通原图的方案数,自然我们就可以 \(dp\) 来考虑这个值。

\(~~~~\) 设 \(f_{S,i}\) 表示 \(S\) 集合内有 \(i\) 条边,不联通的方案数,\(g_{S,i}\) 表示 \(S\) 集合内有 \(i\) 条边,联通的方案数。同时记 \(S\) 集合内部有 \(E_S\) 条边。

\(~~~~\) 显然有 \(f_{S,i}+g_{S,i}=\binom{E_S}{i}\),同时:

\[f_{S,i}=\sum_{T\subset S}\sum_{j=0}^{E_T}g_{T,j}\binom{E_{S-T}}{i-j}~~~~k\in T \]

\(~~~~\) 其中 \(k\) 是一个在 \(S\) 中的固定的点。

\(~~~~\) 那么就随便推推就可以求出这两个数组。然后恰好在第 \(i\) 条边联通可以用 \(i\) 和 \(i-1\) 条边不联通的情况的差来表示 \(i\) 条边恰好联通。

\(~~~~\) 然后我们就可以写出答案:

\[\frac{1}{m+1}\sum_{k=1}^{m}\frac{f_{U,k}}{\binom{m}{k}} \]

代码
查看代码
#include <bits/stdc++.h>
using namespace std;
template<typename T>void read(T &x)
{
    T sig=1;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')sig=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    x*=sig;
}
int Edge[1025];
double C[51][51];
double f[1025][51],g[1025][51];// 不联通/联通 的 选 i 条边的方案数 
vector <int> G[15];
int main() {
	int n,m;read(n);read(m);
	for(int i=0;i<=m;i++)
	{
		C[i][0]=C[i][i]=1;
		for(int j=1;j<i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j];
	}
	for(int i=1,u,v;i<=m;i++)
	{
		read(u);read(v);
		for(int j=0;j<(1<<n);j++)
			if(((j>>(u-1))&1)&&((j>>(v-1))&1)) Edge[j]++;
	}
	g[0][0]=1;
	for(int i=1;i<(1<<n);i++)
	{
		for(int j=0;j<=Edge[i];j++)
		{
			for(int k=(i-1)&i;k;k=(k-1)&i)
			{
				if(k&(i&(-i)))
				{
					for(int l=0;l<=Edge[k];l++)
						f[i][j]+=g[k][l]*C[Edge[i^k]][j-l];//强制钦定某个点作为基准 	
				}
			}
			g[i][j]=C[Edge[i]][j]-f[i][j];
		}
	}
	double Ans=0;int U=(1<<n)-1;
	for(int i=0;i<=m;i++) Ans+=f[U][i]/C[m][i]; 
	printf("%.6f",Ans/(m+1)); 
	return 0;
}
/*
让死亡觊觎我 让恐惧亲吻我
来摧毁我深爱的一切 可仍夺不走我的选择
弹指间湮灭我 但命运打不败活着
让生命如剧烈的烟火 璀璨熄灭前也将点亮
孩童的双眸
*/

按位或

题意

\(~~~~\) 每个时刻有 \(p_i\) 的概率将当前的数字或 \(i\in[0,2^n-1]\),那么期望多少时刻可以将 \(0\) 变为 \(2^n-1\) 或 INF.
\(~~~~\) \(1\leq n\leq 20\).

题解

\(~~~~\) 一道挺nb的题。

\(~~~~\) 根据题意,记答案为 \(n\) 位中每一位第一次被选的最大值的期望,那就不难想到 \(\text{Min-Max}\) 容斥,对应地定义 \(\min(S)\) 为 \(S\) 中每一位第一次被选的最小值,那么:

\[E(\max(U))=\sum_{S\subset U}(-1)^{|T|} E(\min(S)) \]

\(~~~~\) 那么现在的问题就是求:\(E(\min(S))\),我们记 \(P(\min(S)=i)\) 表示 \(S\) 中的元素第一次出现的最小值为 \(i\) 的概率,显然:\(E(\min(S))=\sum_{i=0}^{n-1}P(\min(S=i))i\) .

\(~~~~\) 那么我们就需要求这个概率,强制前 \(i-1\) 次不取得 \(S\) 内,第 \(i\) 次取得 \(S\) 内,当然这两个概率加起来肯定是 \(1\) ,所以我们发现前一个单次的概率就是:

\[\sum_{T\cap S=\empty}P(T) \]

\(~~~~\) 我们不管怎么求,不妨把这个值先记作 \(q\)。那么 \(E(\min(S))=\sum_{i=0}^{n-1}i\times q^{i-1}\times (1-q)\)

\(~~~~\) 运用错位相减和等差数列求和可以求到:\(E(\min(S))=\frac{1}{1-q}\),那我们现在要做的就是求得 \(q\)。

\(~~~~\) 当然,我们发现求的 \(q\) 实际就是求 \(S\) 的补集的子集和,或者说 \(P\) 的超集和,也就是求每个集合的子集和。那怎么做最快?毕竟 \(3^n\) 一脸过不了的样子。

\(~~~~\) 这个时候就来深入发掘一些以前没有注意的性质,比如说 \(\text{FWT}\) 的或卷积。

\(~~~~\) 令 \(C'_{i}=\sum_{j \subset i} C_j\),若 \(C_{i}=\sum_{j~\text{or}~k}A_jB_k\),那么 \(C'_{i}=\sum_{j\subset i,k\subset i} A_jB_k\),因此 \(C'_{i}=A'_{i}B'_{i}\) ,看出来了吗?这就是 \(\text{FWT}\) 的或卷积。

\(~~~~\) 所以就变成 \(\mathcal{O(2^nn)}\) 求了!。

代码
查看代码
#include <bits/stdc++.h>
using namespace std;
template<typename T>void read(T &x)
{
    T f=1;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    x*=f;
}
int n,arr[1048580],PC[1048580];
double p[1048580];
void FWT(double *S)
{
	for(int i=1;i<n;i<<=1)
		for(int j=0;j<n;j+=i<<1)
			for(int k=0;k<i;k++) S[i+j+k]+=S[j+k];
}
int main() {
	read(n);n=1<<n;
	for(int i=0;i<n;i++) scanf("%lf",&p[i]);
	FWT(p);
	for(int i=1;i<n;i++) PC[i]=PC[i>>1]+(i&1);
	double Ans=0;
	for(int i=1;i<n;i++)
	{
		if(1-p[(n-1)^i]<1e-8) return puts("INF")&0;
		Ans+=1/(1-p[(n-1)^i])*(PC[i]&1?1:-1);
	}
	printf("%.9f",Ans);
	return 0;
}

随机算法

\(~~~~\) 以前写的,不想写了。

鲜花

\(~~~~\) 今天是集中发现宝藏歌曲ヾ(@@)ノ

\(~~~~\) 《人是_》(词真的很难记,歌真的很难唱,简单来说就是我不配),但周深nb!!!!!

\(~~~~\) 《给你一瓶魔法药水》 很通俗易懂好吧,词我觉得也没啥问题,以前的词重复的多的是,而且压上韵了才好记好吧,下里巴人不好吗?

\(~~~~\) 《带我去找夜生活》 曲不是很抓耳,但觉得还是可以听。

标签:ch,frac,17,int,sum,fa,2023.1,dp
From: https://www.cnblogs.com/Azazel/p/17060608.html

相关文章