A层邀请赛4
A. 暗之链锁
翻书就好了,在树上差分的第一个例题,书上讲得太好了感觉我写啥都是多余。Cat本来可以1A的,结果把m看成了树边和非树边总共有m条,算贡献的时候算成了n-m...过样例的的时候读入错了我就发现了这个问题,结果改了一处没改第二处……
有大佬说能用树链剖分+线段树,我懒了就没试,我的树链剖分是用来找lca的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5 + 2; const ll mod = 1e9 + 7; int n, m, top[maxn], fa[maxn], siz[maxn], dfn[maxn], rnk[maxn]; int c[maxn], dep[maxn], son[maxn], ntime; ll ans; struct node { int next, to; }a[maxn<<1]; int head[maxn], len; void add(int x, int y) { a[++len].to = y; a[len].next = head[x]; head[x] = len; } void find_heavy_edge(int u, int fat, int depth) { fa[u] = fat; dep[u] = depth; siz[u] = 1; son[u] = 0; int maxsize = 0; for(int i=head[u]; i; i=a[i].next) { int v = a[i].to; if(dep[v]) continue; find_heavy_edge(v, u, depth+1); siz[u] += siz[v]; if(siz[v] > maxsize) { maxsize = siz[v]; son[u] = v; } } } void connect_heavy_edge(int u, int ancestor) { dfn[u] = ++ntime; top[u] = ancestor; rnk[ntime] = u; if(son[u]) { connect_heavy_edge(son[u], ancestor); } for(int i=head[u]; i; i=a[i].next) { int v = a[i].to; if(v == son[u] || v == fa[u]) continue; connect_heavy_edge(v, v); } } int LCA(int x, int y) { while(top[x] != top[y]) { if(dep[top[x]] < dep[top[y]]) swap(x, y); x = fa[top[x]]; } if(dep[x] > dep[y]) swap(x, y); return x; } void dfs(int u) { for(int i=head[u]; i; i=a[i].next) { int v = a[i].to; if(v == fa[u]) continue; dfs(v); c[u] += c[v]; } if(u != 1) { if(c[u] == 0) ans += m; else if(c[u] == 1) ans++; } } int main() { scanf("%d%d", &n, &m); for(int i=1; i<n; i++) { int x, y; scanf("%d%d", &x, &y); add(x, y); add(y, x); } find_heavy_edge(1, 1, 1); connect_heavy_edge(1, 1); for(int i=1; i<=m; i++) { int x, y; scanf("%d%d", &x, &y); c[x]++; c[y]++; int lca = LCA(x, y); c[lca] -= 2; } dfs(1); printf("%lld\n", ans); return 0; }
B. 蚊子
(如果你算错了,就会被liu_runda拿去喂兔子,啊呜~~)
我不会算,兔子你吃了我吧,撑不死你……
所以,根据题解,杀死蚊子的概率不好求,应该求蚊子存活的概率,每只蚊子存活的概率都是(1-p/q)^k(k是路径上在d范围内的节点数),问题就变成了m*(m-1)-所有蚊子存活的概率之和,也就是求出树上任意两个叶子路径的权值乘积(深度>d权值为1,深度<=d权值为存活的概率)。
接下来的树上dp很显然吗我怎么不这么觉得……所以我无奈的去翻了一些其他大佬的题解……
最后对于乘法分配律什么的还是觉得有点感性理解……
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//Cat 对 Chen_jr 的题解情有独钟 #include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 5e6 + 5; const int INF = 0x7fffffff; const ll mod = 1e9 + 7; int n, d; ll qw, g[maxn], p, q, ans, m; ll qpow(ll a, ll b) { ll ans = 1; while(b) { if(b & 1) ans = ans * a % mod; a = a * a % mod; b >>= 1; } return ans; } struct node { int next, to; }a[maxn<<1]; int head[maxn], len; void add(int x, int y) { a[++len].to = y; a[len].next = head[x]; head[x] = len; } char xch,xB[1<<15],*xS=xB,*xTT=xB; #define getc() (xS==xTT&&(xTT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xTT)?0:*xS++) inline int read(){ int x=0,f=1;char ch=getc(); while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } void dfs(int x, int fa, int dep) { //printf("x = %d\n", x); bool is = 1; for(int i=head[x]; i; i=a[i].next) { int v = a[i].to; if(v == fa) continue; dfs(v, x, dep+1); is = 0; g[x] += g[v]; } if(is) g[x] = 1, m++; else g[x] %= mod; ll now = dep <= d ? qw : 1; for(int i=head[x]; i; i=a[i].next) { int v = a[i].to; if(v == fa) continue; ans = (ans+g[v]*(g[x]-g[v]+mod)%mod*now)%mod; } g[x] = g[x]*now%mod; } int main() { //freopen("ww.txt", "r", stdin); n = read(); for(int i=1; i<n; i++) { int x = read(), y = read(); add(x, y); add(y, x); } d = read(), p = read(), q = read(); qw = (1-qpow(q, mod-2)*p%mod+mod)%mod; dfs(1, 1, 0); printf("%lld\n", ((m*(m-1)%mod-ans)%mod+mod)%mod); return 0; }
时光花火,水月星辰