I. Infection
n=2000,我们考虑dp
我们转化题意
发现就是找一个连通子块 然后连通块的权重就是其中任何一个点的a[i]其他都是p[i]
但是对于连通块相接的点 我们都要让他是(1-p[i])
因为有选定源点这一个条件 我们本来的dp[i][j]还要加一维[0/1]
表示 第i个节点 我们子树中有j个感染点 并且是否(1/0)选定了源点
对于转移我们能想到的就是
枚举 i,j 根节点dp[u][i][0/1] 子节点的dp[v][j][0/1]乱搞一下
但是i枚举范围变成sz[u] j枚举范围变成sz[v] 之后时间复杂度是O(n)的
然后我们会枚举n个点所以复杂度就是O(n^2)的 跑的飞快
最后注意我们dp只考虑了子树的 我们最后还要让他的父节点是不准感染的
int qmi(int a, int b, int p){
int res = 1;
while(b){
if(b & 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
int inv(int x){
return qmi(x,mod-2,mod);
}
vector<int>g[N];
int n,a[N],b[N],c[N],p[N],dp[N][2][N],sz[N],ans[N];//dp[i][j][k]表示i节点0/1有源点子树有k个点的概率
void dfs(int u, int fa) {
sz[u]=1;
dp[u][0][1]=p[u];
dp[u][1][1]=a[u];
dp[u][0][0]=(1-p[u]+mod)%mod;
for(auto v:g[u]){
if(v==fa)continue;
dfs(v,u);
sz[u]+=sz[v];
int tmp[2][n+10];
memset(tmp,0,sizeof tmp);
for(int i=1;i<=sz[u]-sz[v];i++){
for(int j=0;j<=sz[v];j++){
(tmp[1][i+j]+=dp[u][1][i]*dp[v][0][j]%mod)%=mod;
(tmp[1][i+j]+=dp[u][0][i]*dp[v][1][j]%mod)%=mod;
(tmp[0][i+j]+=dp[u][0][i]*dp[v][0][j]%mod)%=mod;
}
}
for(int i=1;i<=sz[u];i++){
dp[u][0][i]=tmp[0][i];
dp[u][1][i]=tmp[1][i];
}
}
for(int i=1;i<=n;i++)
(ans[i]+=dp[u][1][i]*(1-p[fa]+mod)%mod)%=mod;
}
void solve() {
cin >> n;
for (int i = 1; i <= n - 1; i++) {
int u, v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
int sum = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i] >> b[i] >> c[i];
sum += a[i];
p[i] = (b[i] * inv(c[i])) % mod;
sum %= mod;
}
for(int i=1;i<=n;i++){
a[i]=a[i]*inv(sum)%mod;
}
dfs(1,0);
for(int i=1;i<=n;i++)cout<<ans[i]<<endl;
}
标签:Onsite,sz,Contest,int,res,Guangzhou,枚举,dp,mod
From: https://www.cnblogs.com/ycllz/p/16892249.html