题目大意: 略
思路 :
看到题目中说有两种边,之后又给出多个点跑来跑去,想到的是树链上的问题。但是由于树链上的点都会影响这条链对答案的贡献,我们可以选择思考是否可以计算一个点或者是一条边对于答案的贡献。想到每个点经过多少次,是一个树链覆盖,想到树上差分。
个人理解这里的差分是有向的,diff[0]代表从该点出发的覆盖, diff[1]代表从该点进入的覆盖,但是树上差分都是表示一个点到根节点(最后统计前缀和的这种顺序), 所以需要两个数组。 代码:
#include <bits/stdc++.h> #include <cstdio> #include <cstring> #include <iostream> #include <vector> #include <queue> #include <algorithm> #include <cmath> #define mem(a,b) memset(a,b,sizeof(a)) #define ll long long #define ull unsigned long long #define PI acos(-1) #define pb(x) push_back(x) #define il inline #define re register #define IO; ios::sync_with_stdio(0);cin.tie(0); //#define ls (o<<1) //#define rs (o<<1|1) #define pii pair<int,int> using namespace std; const int maxn = 1e6+5; const int maxm = 10001000; const int INF = 0x3f3f3f3f; const ll LINF = 3e17+1; const ll mod = 1e9+7; const int MOD = 1e6+7; inline int read(){ register int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return (f==1)?x:-x; } struct edge { int u,v,nxt; bool f; }E[maxn<<1]; // 链式前向星存图 // 由于这里之后要遍历边 u到v 所以存下每个边的u int n, m, head[maxn], eid, a[maxn], diff[2][maxn]; int fa[maxn], son[maxn], siz[maxn], dep[maxn], top[maxn]; void add(int u, int v, bool f) { E[eid] = {u,v,head[u],f}; head[u] = eid++; } // 树剖的dfs1 dfs2 准备lca void dfs1(int u , int f, int deep) { fa[u] = f; dep[u] = deep; siz[u] = 1; int maxson = -1; for (int i = head[u]; i != -1; i = E[i].nxt) { int v = E[i].v; if (v == f) continue ; dfs1(v,u,deep+1); siz[u] += siz[v]; if (siz[v] > maxson) maxson = siz[v], son[u] = v; } } void dfs2(int u, int tp) { top[u] = tp; if (!son[u]) return ; dfs2(son[u], tp); for (int i = head[u]; i != -1; i = E[i].nxt) { int v = E[i].v; if (v == son[u] || v == fa[u]) continue ; dfs2(v,v); } } // 统计树上前缀和 void solve(int u, int f) { for (int i = head[u]; i != -1; i = E[i].nxt) { int v = E[i].v; if (v == f) continue ; solve(v,u); diff[0][u] += diff[0][v]; diff[1][u] += diff[1][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; } // 快速幂等比数列求和算各边贡献 int qpow(int a, int b) { int res = 1; for (; b; b >>= 1) { if (b&1) res = 1ll*res*a%mod; a = 1ll*a*a%mod; } return res; } int main() { n = read(); int u, v;bool f; for (int i = 0; i <= n; i++) head[i] = -1; for (int i = 1; i < n; i++) { u = read(), v = read(), f = read(); add(u,v,f);add(v,u,f); } dfs1(1,0,1);dfs2(1,1); int k = read();int s = 1, t; for (int i = 1; i <= k; i++) { t = read(); int lca = LCA(s, t); // 这里是树上差分的权值在边的情况,边权值赋给深度较深的点 // (还有一种权值在点的情况) diff[0][s]++;diff[0][lca]--; diff[1][t]++;diff[1][lca]--; s = t; } solve(1,0); ll ans = 0; // += 2 存双向边 前一条才是添加的时候的uv顺序 for (int i = 0; i < eid; i += 2) { if (!E[i].f) continue ; u = E[i].u, v = E[i].v; // 确定uv的指向 之后diff就是我上面提到的有向的差分来统计贡献 int cnt = dep[u] > dep[v] ? diff[1][u] : diff[0][v]; ans = ((ans + qpow(2,cnt))%mod + mod - 1)%mod; } printf("%lld\n", ans); }