洛谷 P4637 题解
DaiRuiChen007 · · 题解
洛谷 P4637 题解
思路分析
非常强的思维题,推理过程相当精彩
考虑求出期望的过程:
- 每种引爆地雷的顺序,共有
n! 种情况 - 对于每种顺序,计算出其中实际引爆的地雷个数,也就是所有没有在之前被引爆的地雷个数
- 将每种方案中实际引爆的地雷个数当成操作次数,乘上概率
\dfrac1{n!} ,求和计算期望
由于期望具有线性性,所以我们可以考虑计算每个地雷对答案的贡献,也就是说,对于每个地雷,求出其会在多少种方案中被实际引爆
求出所有可以导致第
所以问题就转化成了:对于第
由于其他的元素在哪里并不重要,所以我们只需要考虑
套用期望的计算公式可以得到:
所以问题就转化成求所有的
可以将所有地雷向其能够直接引爆的点连一条边,我们需要求出对于每个点
考虑缩点,在 DAG 上做拓扑排序转移一个用 bitset 维护的
注意:如果我们直接在原图上做
时间复杂度
代码呈现
#include<bits/stdc++.h>
using namespace std;
const int MAXN=4001;
int p[MAXN],d[MAXN];
vector <int> edge[MAXN],g[MAXN];
bool vis[MAXN];
bitset <MAXN> f[MAXN];
int low[MAXN],dfn[MAXN],dfncnt,sk[MAXN],siz[MAXN],bel[MAXN],deg[MAXN],top,scc;
bool ins[MAXN],link[MAXN][MAXN];
inline void tarjan(int p) {
low[p]=dfn[p]=++dfncnt;
ins[p]=true; sk[++top]=p;
for(int v:edge[p]) {
if(!dfn[v]) {
tarjan(v);
low[p]=min(low[p],low[v]);
} else if(ins[v]) low[p]=min(low[p],low[v]);
}
if(low[p]==dfn[p]) {
++scc;
int k;
do {
k=sk[top--];
bel[k]=scc;
f[scc].set(k);
++siz[scc];
ins[k]=false;
} while(k!=p);
}
}
signed main() {
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d%d",&p[i],&d[i]);
for(int i=1;i<=n;++i) {
for(int j=1;j<=n;++j) {
if(abs(p[i]-p[j])<=d[i]) {
edge[i].push_back(j);
}
}
}
for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;++i) {
for(int j:edge[i]) {
if(bel[i]==bel[j]||link[bel[i]][bel[j]]) continue;
g[bel[i]].push_back(bel[j]);
link[bel[i]][bel[j]]=true;
++deg[bel[j]];
}
}
queue <int> q;
for(int i=1;i<=scc;++i) {
if(!deg[i]) {
q.push(i);
}
}
while(!q.empty()) {
int p=q.front();
q.pop();
for(int v:g[p]) {
f[v]|=f[p];
--deg[v];
if(!deg[v]) q.push(v);
}
}
double res=0;
for(int i=1;i<=n;++i) res+=1.0/((int)f[bel[i]].count());
printf("%.4lf\n",res);
return 0;
}