蚊子(mosquito)
10.26
首先每只蚊子的贡献是独立的.如果一只蚊子经过了k个会被灭蚊器影响的点那么这个蚊子对答案的贡献是1-(1-p/q)k.
用1遍bfs求出哪些点是被灭蚊器影响的点.然后进行不同的处理.
n为树的点数,m为叶子节点数.
算法1:
对于每一只蚊子,做一遍bfs,O(m2)次bfs,复杂度O(nm2),期望得分10分
算法2:
通过一次bfs我们可以统计出一个叶子节点出发的所有蚂蚁的贡献.那么只需要m次bfs就可以了.复杂度O(nm).期望得分30分.
算法3:
对于d=0的测试点3,只有1号点被控制.那么经过了1号点的蚊子会贡献P/Q,我们只需要数一数有多少蚊子经过了1号点.这是非常好数的.期望得分10分.
算法4:
对于p/q=1的测试点4,蚊子只要经过被控制的点就必死无疑.我们只需要统计有多少只蚊子至少经过了一个被控制的点.我们以1为根建树,f[i]表示i所在子树内叶节点的个数,对于每一个被控制的点,我们统计以其为lca的路径条数.复杂度O(n). 这部分分对标算是有提示的.
算法5:
考虑对每一条路径进行讨论,求解lca之后算出这条路径经过的被影响的点数.复杂度应当为O(m2logn),但是细节较多,出题人没有写.
可以通过第6,7个测试点,结合前面的算法可以得到70分.
算法6(满分做法):
考虑对算法4进行拓展.此时每只蚊子的贡献是1-(1-p/q)k.我们可以先不管前面那个1,把后面的(1-p/q)k的和求出来,最后用m*(m-1)减去那个和就可以了.
一条路径可以在LCA处拆成两条.我们考虑如何求出以某个点为LCA的所有路径的贡献之和.一条路径可以拆成两部分,一部分是从一个叶节点走到LCA,另一部分是从LCA走到另一个叶节点.把这两部分看作两条”半路径”.
记g[i]为以i为LCA的所有半路径的贡献之和.(即:对于i子树内的每个叶节点x,g[i]+=(1-p/q)k,k是x到i的半路径上的被控制点的个数).g[i]可以通过O(n)树形DP得到.
接下来我们通过g[i]求出以每个点为LCA的路径的贡献之和.
对于点x,考虑它的所有儿子,每一对儿子(u,v)的贡献是g[u]g[v](1-p/q)
注意,(u,v)和(v,u)都需要算一次.
直接暴力枚举每一对儿子会超时.我们通过乘法分配律可以O(儿子个数)的时间复杂度算出来.总的时间复杂度仍为O(n).空间复杂度也为O(n).但是常数可能较大,因此出题人给了5s的时限.
算法6:
本题属于树上路径统计问题,可以树分治.复杂度O(nlogn).期望得分50分.
可能有选手通过大力卡常数能够直接用树分治AC此题.
思
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#define N 5000005
#define LL long long
#define mod 1000000007
using namespace std;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' | ch > '9') {if(ch == '-')f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
int n, idc, a, b, d, p, q;
int num[N], dep[N], head[N];
LL ans, s, f[N];
LL mpow(LL a, LL b) {
LL rt = 1;
for(; b; b >>= 1, a = a * a % mod)
if(b & 1) rt = rt * a % mod;
return rt;
}
struct Edge{
int to, nxt;
}ed[N << 1];
void adde(int u, int v) {
ed[++idc].to = v;
ed[idc].nxt = head[u];
head[u] = idc;
}
void dfs1(int u, int fa) {
bool flag = 0;
for(int i = head[u]; i; i = ed[i].nxt) {
int v = ed[i].to;
if(v == fa) continue;
flag = 1;
dep[v] = dep[u] + 1;
dfs1(v, u);
num[u] += num[v];
if(dep[u] <= d) f[u] = (f[u] + f[v] * s % mod) % mod;//
}
if( !flag ) num[u] = 1;
if(dep[u] > d) f[u] = num[u];
if(!flag && dep[u] <= d) f[u] = s;
}
void dfs2(int u, int fa) {
for(int i = head[u]; i; i = ed[i].nxt) {
int v = ed[i].to;
if(v == fa) continue;
dfs2(v, u);
LL t1 = f[v] % mod, t2 = f[u];
if( dep[u] <= d ) t2 -= (f[v] * s % mod);//子树内和子树外到达此点的期望个数(两边相乘)
else t2 -= f[v];
t2 = (t2 + mod) % mod;
ans = ( ans + t1 * t2 % mod ) % mod;
}
}
int main() {
freopen ("mosquito.in", "r", stdin);
freopen ("mosquito.out", "w", stdout);
n = read();
for(int i=1; i<n; i++) {
a = read(), b = read();
adde(a, b); adde(b, a);
}
d = read(); p = read(); q = read();
s = 1LL * (q - p) * mpow(q, mod-2) % mod; // s 幸存概率
dfs1(1, 1);
dfs2(1, 1);
LL cc = 1LL * num[1] * (num[1] - 1) % mod;
ans = ((cc - ans) % mod + mod) % mod;
cout << ans << endl;
}