[HDU4035]Maze
壹、题目描述 ¶
贰、题解 ¶
设计状态 \(F(u)\) 表示从 \(u\) 开始,走出去时期望经过的边数,那么我们不难设计出一份代码:
\[\begin{aligned}
1\quad&\textbf{if }u\text{ is a leaf }\textbf{then} \\
2\quad&\quad F(u)=K(u)F(1)+(1-K(u)-E(u))(F(fa(u))+1) \\
3\quad&\textbf{else } \\
4\quad&\quad F(u)=K(u)F(1)+{1-K(u)-E(u)\over D(u)}\left(F(fa)+1+\sum_{v\in son(u)}(F(v)+1)\right)
\end{aligned}
\]
转移有环,而如果使用高斯消元会 \(\rm TLE\),考虑直接硬解:
设 \(F(u)=\alpha K(1)+\beta F(fa(u))+\gamma\),那么
对于叶子节点,有
\[\begin{cases}
&\alpha=K(u) \\
&\beta=1-K(u)-E(u) \\
&\gamma=1-K(u)-E(u)
\end{cases}
\]
对于非叶子节点,有
\[\begin{cases}
&\alpha=K(u) \\
&\beta={1-K(u)-E(u)\over D(u)} \\
&\gamma={{1-K(u)-E(u)\over D(u)}}\sum_{v\in son(u)}(F(v)+1)+{1-K(u)-E(u)\over D(u)}
\end{cases}
\]
对于 \(\gamma\) 进行化简,有
\[\begin{aligned}
\gamma=&{1-K(u)-E(u)\over D(u)}\sum_{v\in son(u)}F(v)+{1-K(u)-E(u)\over D(u)}\times D(u) \\
=&{1-K(u)-E(u)\over D(u)}\sum_{v\in son(u)}\left(\alpha_vF(1)+\beta_vF(fa(v))+\gamma_v\right)+ 1-K(u)-E(u) \\
=&{1-K(u)-E(u)\over D(u)}\sum_{v\in son(u)}\left(\alpha_vF(1)+\beta_vF(u)+\gamma_v\right)+1-K(u)-E(u)
\end{aligned}
\]
将这个带回 \(F(u)\) 的含参式,得到
\[\begin{aligned}
&\left(1-\left({1-K(u)-E(u)\over D(u)}\right)\times ∑\beta_v\right)\times F(u) \\
=&\left(K(u)+\left({1-K(u)-E(u)\over D(u)}\right)\times ∑\alpha_v\right)\times F(1) \\
&+ {1-K(u)-E(u)\over D(u)}\times F(fa(u))\\
&+(1-K(u)-E(u)) + {1-K(u)-E(u)\over D(u)}\times ∑\gamma_v
\end{aligned}
\]
此时,我们可以直接算 \(\alpha_u,\beta_u,\gamma_u\) 了,具体来说就是将 \(F(u)\) 的系数除到右边去,然后就得到了 \(F(u)=\alpha F(1)+\beta F(fa(u))+\gamma\) 的形式的式子,并且这些系数与 \(F\) 无关。
对于根节点,有 \(F(1)=\alpha F(1)+\gamma\),得到
\[F(1)={\gamma_1\over 1-\alpha_1}
\]
如果 \(\alpha_1\rightarrow 1\) 或者中途某一步除数为 \(0\) 就无解了。
时间复杂度 \(\mathcal (Tn)\).
叁、参考代码 ¶
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define NDEBUG
#include<cassert>
namespace Elaina{
#define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
#define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define mmset(a, b) memset(a, b, sizeof a)
// #define int long long
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
template<class T>inline T fab(T x){ return x<0? -x: x; }
template<class T>inline void getmin(T& x, const T rhs){ x=min(x, rhs); }
template<class T>inline void getmax(T& x, const T rhs){ x=max(x, rhs); }
template<class T>inline T readin(T x){
x=0; int f=0; char c;
while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
return f? -x: x;
}
template<class T>inline void writc(T x, char s='\n'){
static int fwri_sta[1005], fwri_ed=0;
if(x<0) putchar('-'), x=-x;
do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
putchar(s);
}
}
using namespace Elaina;
const int maxn=1e4;
const double eps=1e-10;
inline int compare(double x, double y){
if(fab(x-y)<eps) return 0;
if(x>y) return 1;
return -1;
}
vector<int>g[maxn+5];
double K[maxn+5], E[maxn+5];
int d[maxn+5], n;
inline void add_edge(int u, int v){
g[u].push_back(v); ++d[u];
g[v].push_back(u); ++d[v];
}
inline void input(){
n=readin(1);
for(int i=1; i<=n; ++i)
g[i].clear(), d[i]=0;
int u, v;
for(int i=1; i<n; ++i){
u=readin(1), v=readin(1);
add_edge(u, v);
}
int k, e;
for(int i=1; i<=n; ++i){
k=readin(1), e=readin(1);
K[i]=1.0*k/100, E[i]=1.0*e/100;
}
}
double alpha[maxn+5], beta[maxn+5], gamma[maxn+5];
bool dfs(int u, int par){
if(d[u]==1 && u!=1){ // is a leaf
alpha[u]=K[u];
beta[u]=gamma[u]=1-K[u]-E[u];
// printf("alpha %d == %.6f\nbeta %d == %.6f\ngamma %d == %.6f\n", u, alpha[u], u, beta[u], u, gamma[u]);
return true;
}
double sumAlpha=0, sumBeta=0, sumGamma=0;
for(int v: g[u]) if(v!=par){
if(!dfs(v, u)) return false;
sumAlpha+=alpha[v];
sumBeta+=beta[v];
sumGamma+=gamma[v];
}
double C=(1-K[u]-E[u])/d[u];
double A=1-sumBeta*C;
double B=K[u]+sumAlpha*C;
double D=1-K[u]-E[u]+C*sumGamma;
if(compare(A, 0)==0) return false;
alpha[u]=B/A, beta[u]=C/A, gamma[u]=D/A;
return true;
}
signed main(){
rep(_, 1, readin(1)){
input();
printf("Case %d: ", _);
if(!dfs(1, 0)) printf("impossible\n");
else if(compare(alpha[1], 1)==0) printf("impossible\n");
else printf("%.6f\n", gamma[1]/(1-alpha[1]));
}
return 0;
}
肆、关键之处 ¶
对于这种转移存在环,但是又非 \(\mathcal O(n^3)\) 的数据范围时,一种常用套路是将父节点与子节点分离开,然后设参暴力算。