E - Roulettes
题意:有N个转盘,每个转盘上有Pi个点数,转动一次转盘花费Ci,每次转动得到该转盘上的点数是等可能的,求获得M点所需要的最小花费。(概率dp)
关键:1.定义状态dp[i]为获得点数大于等于i所需要的最少花费
2.对于每个状态dp[i],对于当前某一个转盘,可以从dp[i-p1]...dp[i-pi]转移过来
3.由于转盘上可能有为0的点,故需要移项得到公式。
代码实现
#include <string>
#include <algorithm>
#include <math.h>
#include <cmath>
#include <set>
#include <queue>
#include <cstring>
#include <map>
#include<iomanip>
#include<iostream>
#include<stack>
#include<unordered_set>
#define io ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
using namespace std;
const int N = 1e2 + 10;
int n, m;
int c[N], p[N];
int s[N][N];
double dp[N];
int main()
{
io;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> c[i] >> p[i];
for (int j = 1; j <= p[i]; j++)
{
cin >> s[i][j];
}
}
for (int i = 1; i <= m; i++)
{
dp[i] = 1e9;
for (int j = 1; j <= n; j++)
{
int p1 = 0;
double p2 = 0;
for (int k = 1; k <= p[j]; k++)
{
if (!s[j][k])
p1++;
else
p2 += dp[max(0, i - s[j][k])];
}
dp[i] = min(dp[i], (p2 + c[j] * p[j]) / (p[j] - p1));
}
}
cout << fixed << setprecision(6) << dp[m];
return 0;
}
F - A Certain Game
题意:有N个玩家,初始时每个人是一支队伍,每次选择两人p、q所在的队伍(保证p、q两人不在同一支队伍中)对战,设a为p的队伍成员数量,b为q的队伍成员数量,那么p所在的队伍获胜的概率为(a/(a+b)),q所在的队伍获胜的概率为(b/(a+b))。两支队伍对战后,合并为1支队伍,问经过N-1次对战后,每个人获胜场次的期望是多少。
关键:1.对决结果可以用一棵生成树来表示,这颗树的叶子节点表示每个玩家,边的权值表示每场对决产生的期望胜场。那么我们每产生一场对决就生成一个新的节点,代表新合并的队伍,并建立两条边连向对决的两只队伍,边的权值为期望胜场。每个玩家最终的期望获胜场次就是从根节点走到叶节点的路径权值之和。我们可以通过并查集维护集合关系,最后跑一遍dfs就可以得到答案;
代码实现
#include <string>
#include <algorithm>
#include <math.h>
#include <cmath>
#include <set>
#include <queue>
#include <cstring>
#include <map>
#include<iomanip>
#include<iostream>
#include<stack>
#include<unordered_set>
#define io ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
using namespace std;
typedef long long ll;
const int N = 3*(2e5 + 8);
const ll mod = 998244353;
int f[N],siz[N];
int h[N], e[N],ne[N], idx;ll w[N];
ll ans[N];
bool vis[N];
ll inv(ll a, ll b)
{
ll ans = 1;
while (b)
{
if (b & 1)
ans = (ans * a) % mod;
b >>= 1;
a = (a * a) % mod;
}
return ans;
}
int find(int k)
{
if (f[k] == k)
return k;
return f[k] = find(f[k]);
}
void add(int a, int b, ll c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u)
{
vis[u] = true;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
ll v = w[i];
if (!vis[j])
{
ans[j] += (ans[u] + v) % mod;
dfs(j);
}
}
}
int main()
{
io;
int n;
cin >> n;
int node = n;
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i++)
{
f[i] = i;
siz[i] = 1;
}
for (int i = 1; i < n; i++)
{
int p, q;
cin >> p >> q;
int fp = find(p), fq = find(q);
ll a = siz[fp], b = siz[fq];
ll v = inv(a + b, mod - 2);
++node;
f[node] = node;
add(node, fp, a * v), add(node, fq, b * v);
f[fq] = f[fp] = node;
siz[node] = siz[fp] + siz[fq];
}
dfs(node);
for (int i = 1; i <= n; i++)
cout << ans[i] << " ";
return 0;
}