题目:https://codeforces.com/problemset/problem/192/E
题意:给m条路径,依次输出每条边经过的次数。
思路:边权下推到点,套板子。由于最后是单点询问,可以直接用树上差分,每次给u到v的路径,u++, v++, lca(u, v)-=2,最后dfs遍历一遍即可。
代码:
#include <bits/stdc++.h>
#define ls rt<<1
#define rs rt<<1|1
#define LL long long
using namespace std;
const int maxn = 1e5+5;
int n;
std::vector<int> E[maxn];
int f[maxn], top[maxn], id[maxn], rk[maxn], siz[maxn], d[maxn], son[maxn], cnt;
void dfs1(int u, int fa){
d[u] = d[fa] + 1; f[u] = fa; siz[u] = 1; son[u] = 0;
for(auto v : E[u]){
if(v == fa) continue;
dfs1(v, u);
siz[u] += siz[v];
if(siz[son[u]] < siz[v]) son[u] = v;
}
}
void dfs2(int u, int rt){
id[u] = ++cnt; top[u] = rt;
if(son[u]) dfs2(son[u], rt);
for(auto v : E[u]){
if(v == f[u] || v == son[u]) continue;
dfs2(v, v);
}
}
int sum[maxn<<2], tag[maxn<<2];
void pd(int rt, int l, int r){
if(tag[rt]) {
int mid = l+r >> 1;
tag[ls] += tag[rt]; tag[rs] += tag[rt];
sum[ls] += (mid-l+1)*tag[rt]; sum[rs] += (r-mid)*tag[rt];
tag[rt] = 0;
}
}
void upd(int rt, int l, int r, int L, int R, int val){
if(L <= l && R >= r){
tag[rt] += val;
sum[rt] += (r-l+1)*val;
return ;
}int mid = l+r >> 1; pd(rt, l, r);
if(L <= mid) upd(ls, l, mid, L, R, val);
if(R > mid) upd(rs, mid+1, r, L, R, val);
sum[rt] = sum[ls] + sum[rs];
}
int qry(int rt, int l, int r, int L, int R){
if(L <= l && R >= r) return sum[rt];
int mid = l+r >> 1, res = 0; pd(rt, l, r);
if(L <= mid) res += qry(ls, l, mid, L, R);
if(R > mid) res += qry(rs, mid+1, r, L, R);
return res;
}
void Update(int u, int v){
int fu = top[u], fv = top[v];
while(fu != fv){
if(d[fu] < d[fv]) swap(fu, fv), swap(u, v);
upd(1, 1, n, id[fu], id[u], 1);
u = f[fu]; fu = top[u];
}
if(u == v) return ;
if(d[u] > d[v]) swap(u, v);
upd(1, 1, n, id[son[u]], id[v], 1);
}
int e[maxn][2], m;
int main(){
while(cin >> n){
int u, v;
for(int i=1; i<n; i++){
cin >> u >> v;
e[i][0] = u, e[i][1] = v;
E[u].push_back(v); E[v].push_back(u);
}
dfs1(1, 0); dfs2(1, 1);
for(int i=1; i<n; i++) if(d[e[i][0]] < d[e[i][1]]) swap(e[i][0], e[i][1]);
cin >> m;
while(m--){
cin >> u >> v;
Update(u, v);
}
for(int i=1; i<n; i++) printf("%d%c", qry(1, 1, n, id[e[i][0]], id[e[i][0]]), " \n"[i==n-1]);
}
}
LCA+树上差分写法
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
std::vector<int> E[maxn];
int f[maxn], top[maxn], siz[maxn], d[maxn], son[maxn], cnt;
void dfs1(int u, int fa){
d[u] = d[fa] + 1; f[u] = fa; siz[u] = 1; son[u] = 0;
for(auto v : E[u]){
if(v == fa) continue;
dfs1(v, u);
siz[u] += siz[v];
if(siz[son[u]] < siz[v]) son[u] = v;
}
}
void dfs2(int u, int rt){
top[u] = rt;
if(son[u]) dfs2(son[u], rt);
for(auto v : E[u]){
if(v == f[u] || v == son[u]) continue;
dfs2(v, v);
}
}
int lca(int u, int v){
while(top[u] != top[v]){
if(d[top[u]] >= d[top[v]]) u = f[top[u]];
else v = f[top[v]];
}
return (d[u]<d[v] ? u : v);
}
int a[maxn], e[maxn][2];
void dfs(int u, int fa){
for(auto v : E[u]) {
if(v == fa) continue;
dfs(v, u);
a[u] += a[v];
}
}
int main(){
int n, m, u, v;
scanf("%d", &n);
for(int i=1; i<n; i++) {
scanf("%d%d", &u, &v);
E[u].push_back(v), E[v].push_back(u);
e[i][0] = u, e[i][1] = v;
}
dfs1(1, 0); dfs2(1, 1);
for(int i=1; i<n; i++) if(d[e[i][0]] < d[e[i][1]]) swap(e[i][0], e[i][1]);
cin >> m;
while(m--){
scanf("%d%d", &u, &v);
a[u] ++, a[v] ++, a[lca(u, v)] -= 2;
}
dfs(1, 0);
//for(int i=1; i<=n; i++) cout << a[i] << " ";cout << endl;
for(int i=1; i<n; i++) printf("%d%c", a[e[i][0]], " \n"[i==n-1]);
}