题解
题意:n个地点,n-1条边,有单向也有双向的,给出k个连续到达的地点,允许路上逆行,但是要缴纳罚款,每次缴纳的罚款是上一次经过此地的两倍,问到达所有目的地的最小罚款金额
诶,补题补题…
看别人题解看到的,本题等价于问:
一颗树,无向边边权0,有向边边权1,逆向行走会使边权*2,问最小的边权和
做法:
建图建双向边,方便树上统计,无向边正向边反向边边权都是0,有向边正向边设置1,反向边设置-1,
先bfs处理树上关系,方便后面使用lca查树链
再树上差分标记树上经过的所有的点,由于有的边有方向,遂,将分成两颗树,一个统计自顶向下到达当前节点时的逆向罚款,一个统计自底向上时的逆向罚款,
多次经过同一个地点造成的罚款总额 = 1 + 2 1 + 2 3 + . . . + 2 p − 1 = 2 p − 1 =1+2^1+2^3+...+2^{p-1}=2^p-1 =1+21+23+...+2p−1=2p−1,最后统计所有逆向行驶的罚款
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
int n, m, k;
struct egde {
int to, next, w;
} e[N];
int head[N], tot;
int up[N];// 到达该点时的类型 1 单行道 可以从该点出去 -1 单行道 不能从该点出去 0 都可以
void add(int u, int v, int w) {
e[++tot] = {v, head[u], w};
head[u] = tot;
}
const int Depth = 20;
queue<int> q;
int d[N];
int f[N][Depth + 10];
void bfs(int root) { // bfs初始化lca的f[][]
queue<int> q;
d[root] = 0;
f[root][0] = root;
up[root] = 0;
q.push(root);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = 1; i <= Depth; ++i) {
f[u][i] = f[f[u][i - 1]][i - 1];
}
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
if (v == f[u][0])continue;
d[v] = d[u] + 1;
f[v][0] = u;
up[v] = -e[i].w;//由于是树 所以不会被覆盖
q.push(v);
}
}
}
int lca(int u, int v) {
if (d[u] < d[v])swap(u, v);
for (int i = Depth; i >= 0; --i) {
if (d[f[u][i]] >= d[v]) u = f[u][i];
}
if (u == v) return u;
for (int i = Depth; i >= 0; --i) {
if (f[u][i] != f[v][i]) {
u = f[u][i];
v = f[v][i];
}
}
return f[u][0];
}
int cnt1[N], cnt2[N];
void dfs(int u) {
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
if (v == f[u][0])continue;
dfs(v);
cnt1[u] += cnt1[v];//树上差分
cnt2[u] += cnt2[v];
}
}
ll qpow(ll a, ll b) {
ll res = 1;
while (b) {
if (b & 1)res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int main() {
ios::sync_with_stdio(0);
memset(head, -1, sizeof(head));
cin >> n;
for (int i = 1, u, v, w; i < n; ++i) {
cin >> u >> v >> w;
add(u, v, w);
add(v, u, -w);
}
bfs(1);//起点从1开始
cin >> k;
int last = 1, now;
for (int i = 1; i <= k; ++i) {
cin >> now;
if (last == now)continue;
int top = lca(last, now);
cnt1[last]++;//自底向上
cnt1[top]--;
cnt2[now]++;//自顶向下
cnt2[top]--;
last = now;
}
dfs(1);
ll ans = 0;//统计罚款的次数
for (int i = 1; i <= n; ++i) {
if (!up[i]) continue;
if (up[i] == -1) {
ans = (ans + qpow(2, cnt1[i]) - 1) % mod;
} else {
ans = (ans + qpow(2, cnt2[i]) - 1) % mod;
}
}
cout << ((ans + mod) % mod) << endl;
return 0;
}