【woj4747】最短路

太妙了卧槽

首先除了1之外其它n-1个点全都等价。

我们假装选定了一个目标点。将它拎出来。

剩下还有n-1个点。这些点在原图上会构成一个最短路树。

我们通过动态规划来算这个树。

我们设f[i][j]表示最后一层用了j个点,当前已经用了i个点的概率。e[i][j]表示期望。

我们可以通过枚举下一层用了多少个点来转移这个概率和期望。

我们考虑转移条件。

首先,下一层任何一个点都得和本层中的某个点连边。

其次,除下一层之外不能再有任何一个点和本层点连边。否则他们也会加入下一层。

第一个概率是(1-{不通概率}^{本层点数})^{下层点数}。设p为联通概率,本层点j个,下层点k个。则第一个概率表达为

(1-(1-p)^j)^k总概率减去和本层点都不联通的概率再自乘k次。

第二个概率是(1-p)^{j*(n-i-k)},因为这两部分点任何点对都不能有。

与此同时,点号对答案有贡献。所以要从(n-i-1),即减去已经使用和目标答案点之外的剩下点钟,选出k个来给这层标号。

所以概率相乘再乘上一个\binom{n-i-1}{k}。而期望的转移本应该是转移概率*(上层期望+1),打开之后发现是转移概率*上层期望+转移概率。因为这道题边权为1比较特殊。

所以直接乘上转移。

统计答案的时候分两部分考虑。一个是联通答案。一个是不连通的1e9答案。

我们不需要枚举从第几层转移过来。因为这些都在转移过程中计算成概率和期望了。我们只需要管总共用了几个点,最后一层有几个点。

对于一个(i,j),最后一层j个点只要有一个和当前点相连就能得到贡献。

所以概率是(1-p^j)。同样,也要乘上(概率+期望)。

如果一个都不相连,而本层是最后一层,不能产生下一层。所以n-i个点和这j个点不能产生任何一条边。

概率(1-p)^{(n-i)*j},贡献是1e9。

这道题的思想本身是bfs树概率期望,我将之视为最短路树,按深度分层。计算出所有可能的概率和期望,最后假设一个最后一层。统计答案。

这种思想值得学习,在点数上思考。

我们发现楼上好像没有直接用p的幂,那就直接预处理(1-p)的幂就可以了。

其它两道题好像不太可做的鸭子,,

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define in read()
int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();if(ch=='-')f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;
		ch=getchar();
	}return cnt*f;
}

int n,p;
int ri[200003];
int f[403][403],e[403][403],c[403][403];
const int mod=998244353;
int inf=1e9;
int add(int a,int b){
	if(a+b>mod)return a+b-mod;return a+b;
}
int dec(int a,int b){
	if(a-b<0)return a-b+mod;return a-b; 
}
int mul(int a,int b){
	return a*b%mod;
}
int ksm(int a,int b){
	int sum=1;
	while(b){
		if(b&1)sum=mul(sum,a);a=mul(a,a);b>>=1;
	}return sum;
}

signed main(){
	n=in;p=in;p=mul(p,ksm(1000000,mod-2));
	for(int i=0;i<=n;i++)c[i][0]=1;
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)c[i][j]=add(c[i-1][j],c[i-1][j-1]);
	ri[0]=1;for(int i=1;i<=200000;i++)ri[i]=ri[i-1]*(1+mod-p)%mod;
	f[1][1]=1;e[1][1]=0;
	for(int i=1;i<=n-1;i++){
		for(int j=1;j<=i;j++){
			for(int k=1;k<=n-i-1;k++){
				int gu=mul(mul(ksm(dec(1,ri[j]),k),ri[j*(n-i-k)]),c[n-i-1][k]);
				f[i+k][k]=add(f[i+k][k],mul(gu,f[i][j]));
				e[i+k][k]=add(e[i+k][k],mul(gu,add(f[i][j],e[i][j])));
			}
		}
	}int ans=0;
	for(int i=1;i<=n-1;i++){
		for(int j=1;j<=i;j++){
			ans=add(ans,mul(dec(1,ri[j]),add(f[i][j],e[i][j])));
			ans=add(ans,mul(ri[(n-i)*j],mul(f[i][j],inf%mod)));
		}
	}
	cout<<mul(ans,mul(n-1,ksm(10,6*n*n)));
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值