[SHOI2014]概率充电器
树形概率期望dp
充上电的概率不好统计,但是充不上电的概率还是比较容易统计的。用(f[x])表示(x)节点充不上电的概率,那么(now)节点能充上电的期望就是(1-f[now]),定义(ff[x,y])表示边(x,y)不通的概率,
对于每一个节点(now)能够从子树及自己都无法充上电的转移是
由父节点的转移其实本质上是一样的,但是父节点的概率还包含一部分(now)本身的概率,所以要除去,由上一个方程解出就是(f[fa]/(f[now]+(1-f[now])*ff[now]))
表示的是(now)的父亲(fa)因为初了(now)的其他原因充不上电的原因,用(d)来代替的话转移式就变成(f[now]*=d+(1-d)*ff[now]),和第一个转移式是一样的
#include<iostream>
#include<cstdio>
#include<cstring>
#define M 1000021
using namespace std;
int i,m,n,j,k,a[M],ver[M],edge[M],head[M],nex[M],cnt,x,y,z;
double f[M],v[M],ans,ff[M];
inline int gtt()
{
char c=getchar();
int x=0,r=1;
while(c<'0'||c>'9')
{
if(c=='-') r=-1;
c=getchar();
}
while(c>='0'&&c<='9')
x=(x<<3)+(x<<1)+c-48,c=getchar();
return x*r;
}
void add(int x,int y,int z)
{
cnt+=1;
ver[cnt]=y; nex[cnt]=head[x]; head[x]=cnt; edge[cnt]=z;
}
void dfs1(int now,int fa)
{
f[now]=((double)100-a[now])/100;
for(int i=head[now];i;i=nex[i])
{
int t=ver[i];
if(t==fa) continue;
dfs1(t,now);
ff[t]=(double)(100-edge[i])/100;
f[now]*=f[t]+(1-f[t])*ff[t];
}
}
void dfs2(int now,int fa)
{
long double d=f[fa]/(f[now]+(1-f[now])*ff[now]);
if(fa)f[now]*=d+(1-d)*ff[now];
for(int i=head[now];i;i=nex[i])
{
int t=ver[i];
if(t==fa) continue;
else dfs2(t,now);
}
ans+=1-(f[now]);
}
int main()
{
n=gtt();
for(i=1;i<n;++i)
{
x=gtt(); y=gtt(); z=gtt();
add(x,y,z); add(y,x,z);
}
for(i=1;i<=n;++i) a[i]=gtt();
dfs1(1,0);
dfs2(1,0);
printf("%.6lf",ans);
}
2538. 「PKUWC2018」Slay the Spire
保证强化牌上的数字都大于 1。
这很妙啊
这样的话打出一张强化牌一张攻击牌肯定不劣于打出最大的两张攻击牌
那么只要强化牌大于等于k-1张就选择最大的k-1张强化牌和最大的攻击牌
否则就打出全部强化牌后按顺序打出最大的攻击牌
然后就可以dp了啊qwq
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define add(x,y) (x+y>=P?x+y-P:x+y)
using namespace std;
const int M=10001;
const int P=998244353;
char gc()
{
static char now[1<<22], *S,*T;
if(S==T)
{
T=(S=now)+fread(now,1,1<<22,stdin);
if(S==T) return EOF;
}
return *S++;
}
int gi()
{
int x=0;
char c=gc();
while(c>'9' || c<'0') c=gc();
while(c<='9' && c>='0') x=x*10+c-'0', c=gc();
return x;
}
int s[10];
void ptt(int x)
{
s[0]=0;
while(x) s[++s[0]]=x%10, x/=10;
for(int i=s[0];i>=1;i--) putchar(s[i]+'0');
if(!s[0]) putchar('0');
putchar(10);
}
int n,m,k,a[M],b[M],T,w[M],A[M],B[M],inv[M];
int f[3010][3010],d[3010][3010],e[3010][3010],g[3010][3010];
LL res;
bool cmp(int a,int b){return a>b;}
LL C(int n,int m) {if(m<=n) return (LL)A[n]*B[m]%P*B[n-m]%P; return 0;}
int main()
{
A[0]=B[0]=inv[0]=inv[1]=1;
for(int i=1;i<=3010;i++) A[i]=(LL)A[i-1]*i%P;
for(int i=2;i<=3010;i++) inv[i]=(LL)((LL)P-P/i)*inv[P%i]%P;
for(int i=1;i<=3010;i++) B[i]=(LL)B[i-1]*inv[i]%P;
T=gi();
for(;T;T--)
{
memset(f,0,sizeof(f));
memset(d,0,sizeof(d));
memset(g,0,sizeof(g));
memset(e,0,sizeof(e));
res=0;
n=gi(), m=gi(), k=gi();
for(int i=1;i<=n;i++) a[i]=gi();
for(int i=1;i<=n;i++) w[i]=gi();
sort(a+1,a+1+n,cmp); sort(w+1,w+1+n,cmp);
//d[0][0]=1;
for(int i=0;i<=n;i++) e[0][i]=1;
for(int i=1;i<k;i++)
{
for(int j=1;j<=n;j++) d[i][j]=1ll*e[i-1][j-1]*a[j]%P;
for(int j=1;j<=n;j++) e[i][j]=add(e[i][j-1],d[i][j]);
}
for(int i=1;i<=k;i++)
{
for(int j=i;j<=n;j++) f[i][j]=(g[i-1][j]+w[j]*C(j-1,i-1)%P)%P;
for(int j=i;j<=n;j++) g[i][j]=add(g[i][j-1],f[i][j-1]);
}
memset(g,0,sizeof(g));
for(int i=1;i<=k;i++)
{
for(int j=i;j<=n;j++) f[i][j]=f[i][j]*C(n-j,m-k)%P;
for(int j=i;j<=n;j++) g[i][j]=add(g[i][j-1],f[i][j]);
}
for(int i=0;i<k;++i)
if(e[i][n]&&g[k-i][n])res=(res+(LL)e[i][n]*g[k-i][n])%P;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++) f[i][j]=w[j]*C(n-j,i-1)%P;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++) g[i][0]=add(g[i][0],f[i][j]);
if(k==1)for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++) d[i][0]=C(n,i);
else
{
for(int i=k;i<=m;i++)
for(int j=1;j<=n;j++) e[i][j] =(LL)d[k-1][j]*C(n-j,i-k+1)%P;
for(int i=k;i<=m;i++)
for(int j=1;j<=n;j++) d[i][0]=add(d[i][0],e[i][j]);
}
for(int i=k;i<m;i++) res=(res+(LL)d[i][0]*g[m-i][0])%P;
ptt(res);
}
}
孤立集
对于一个正整数集合(S),如果存在(kin S)且满足(k-1 otin S)且(k+1 otin S),则称(k)为(S)中的一个「孤立元」。
给定一个集合(A={1,2,cdots,n}),并等概率的选出它的一个恰好含有(m)个元素的子集(B),你的任务是,计算(B)中所有孤立元的和的数学期望值。
很容易想到对于(1,n)被选为孤立集的概率是一样的,([2,n-1])被选为孤立集的概率是一样的
先考虑1和n:被选的概率是(frac{m}{n}) 要使n不是孤立集需要满足(n-1)同时被选,不是孤立集概率(frac{m}{n}frac{m-1}{n-1}),那么这一部分的期望就是((1+n)(frac m n-frac{m}{n}frac{m-1}{n-1}))画一下柿子((1+n)frac m nfrac{n-m}{n-1})
然后是([2,n-1]):(forall k in [2,n-1])被选的概率同样是(frac{m}{n})而由于左右两边被选都不合法,所以是孤立集的概率就是
画柿子后 $$frac m n frac{n-2 imes m+1}{n-1}$$
#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
#define M 998244353
using namespace std;
LL i,m,n,j,k,t,c[2000001],inv[2000001];
int main()
{
scanf("%lld",&t); inv[1]=1;
for(int i=1;i<=1500000;i++) c[i]=(c[i-1]+i)%M;
for(int i=2;i<=1500000;i++) inv[i]=(M-M/i)%M*inv[M%i]%M;
for(;t;t--)
{
scanf("%lld%lld",&n,&m);
if(m==1)
{
printf("%lld
",c[n]*inv[n]%M);
continue;
}
if(n==m || m==0)
{
printf("0
");
continue;
}
LL ans=( (m*inv[n] %M*(n-m) %M*inv[n-1] %M*(n+1) %M)%M +((m*inv[n] %M*(n-2*m+1) %M*inv[n-1] %M +m*inv[n] %M*(m-1) %M*inv[n-1] %M*(m-2) %M*inv[n-2] %M)%M*(c[n-1]-1)%M)%M)%M;
ans%=M;
if(ans<0) ans+=M;
printf("%lld
",ans);
}
}