A层邀请赛4
A. 暗之链锁
树剖维护一下即可,其实树上差分也可
注意线段树空间开四倍!!!!!!!!!!
左移\(2\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c >= '0' && c <= '9');
return x;
}
const int maxn = 100005;
int head[maxn],tot,n,m;
struct edge{
int to, net;
}e[maxn << 1 | 1];
void add(int u, int v){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
}
int id[maxn],dep[maxn],fa[maxn],son[maxn],top[maxn],size[maxn],nid[maxn];
int cnt[maxn];
struct tree{
int t[maxn << 2 | 1];
void push_down(int x){
t[x << 1] += t[x];
t[x << 1 | 1] += t[x];
t[x] = 0;
}
void modify(int x,int l, int r, int L, int R){
if(L <= l && r <= R){
++t[x];
return;
}
if(t[x])push_down(x);
int mid = ( l + r) >> 1;
if(L <= mid)modify(x << 1, l, mid, L, R);
if(R > mid)modify(x << 1 | 1, mid + 1, r, L , R);
}
void query(int x, int l, int r){
if(l == r){
cnt[nid[l]] = t[x];
return;
}
if(t[x])push_down(x);
int mid = (l + r) >> 1;
query(x << 1, l ,mid);
query(x << 1 | 1, mid + 1, r);
}
}T;
void dfs1(int x){
size[x] = 1;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == fa[x])continue;
dep[v] = dep[x] + 1;
fa[v] = x;
dfs1(v);
size[x] += size[v];
if(size[v] > size[son[x]])son[x] = v;
}
}
int tim;
void dfs2(int x, int tp){
id[x] = ++tim; nid[tim] = x;
top[x] = tp;
if(son[x])dfs2(son[x],tp);
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(v == fa[x] || v == son[x])continue;
dfs2(v, v);
}
}
void work(){
int u = read(), v = read();
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]])swap(u, v);
T.modify(1, 1, n, id[top[u]], id[u]);
u = fa[top[u]];
}
if(u == v)return;
if(dep[u] < dep[v])swap(u ,v);
T.modify(1, 1, n, id[v] + 1, id[u]);
}
int main(){
n = read(), m = read();
for(int i = 1; i < n; ++i){
int u = read(), v = read();
add(u, v);add(v, u);
}
dep[1] = fa[1] = 1;dfs1(1);dfs2(1,1);
for(int i = 1; i <= m; ++i)work();
T.query(1, 1, n);
ll ans = 0;
for(int i = 2; i <= n; ++i){
if(cnt[i] == 1)++ans;
if(cnt[i] == 0)ans += m;
}
printf("%lld\n",ans);
return 0;
}
B. 蚊子
这题告诉我优秀的题意转化是多么重要
杀死的概率不好求,但是存活的概率好求,用总数减去存活的期望就是杀死的期望
那么如何求存活的期望,发现问题可以转化为求树上任意两个叶子的路径的权值乘积,其中深度\(>=d\)的权值为1,小于等于\(d\)的权值为每次存活的概率
然后树\(DP\)就比较显然了,请读者自证
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
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;
}
const int maxn = 5000005;
const int mod = 1e9 + 7;
int head[maxn],tot,n;
struct edge{
int to, net;
}e[maxn << 1 | 1];
void add(int u, int v){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
}
ll qpow(ll x, ll y){
ll ans = 1;
for(;y;y >>= 1, x = x * x % mod)if(y & 1)ans = ans * x % mod;
return ans;
}
int d;
ll qw, g[maxn], p, q, ans, m;
void dfs(int x, int fa, int dep){
bool is = 1;
for(register int i = head[x]; i; i = e[i].net){
int v = e[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(register int i = head[x]; i; i = e[i].net){
int v = e[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(){
n = read();
for(register int i = 1; i < n; ++i){
int u = read(), v = read();
add(u, v);add(v, u);
}
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;
}