题意:
给定一棵有 个结点的树,边带权,求有多少对结点 满足 到 的路径长度不大于 且 路径上的边权和不大于 。
链接:
https://vjudge.net/problem/CodeForces-293E
解题思路:
点分治, 子问题即求类似二维偏序的问题,先一维排序,用双指针,另一维用树状数组统计答案。
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
vector<pii> G[maxn];
pii dis[maxn];
int siz[maxn], vis[maxn], rub[maxn];
int n, L, W, tn, rmn, rt, tot, top;
ll ans;
struct BIT{
int c[maxn];
#define lowb(x) ((x)&(-x))
void update(int x, int val){
++x;
while(x <= n) c[x] += val, x += lowb(x);
}
int query(int x){
int ret = 0; ++x; x = min(x, n);
while(x > 0) ret += c[x], x -= lowb(x);
return ret;
}
} c;
void getRt(int u, int f){
int mx = 0; siz[u] = 1;
for(auto &e : G[u]){
int v = e.second, w = e.first;
if(v == f || vis[v]) continue;
getRt(v, u);
siz[u] += siz[v];
mx = max(mx, siz[v]);
}
mx = max(mx, tn - siz[u]);
if(mx < rmn) rmn = mx, rt = u;
}
void dfs(int u, int f, int cnt, int d){
dis[++tot] = {d, cnt};
for(auto &e : G[u]){
int v = e.second, w = e.first;
if(v == f || vis[v]) continue;
dfs(v, u, cnt + 1, d + w);
}
}
void cal(int u, int cnt, int d, int flg){
tot = 0;
dfs(u, 0, cnt, d);
sort(dis + 1, dis + 1 + tot);
for(int l = 1, r = tot; r >= 1; --r){
while(l <= tot && dis[r].first + dis[l].first <= W){
c.update(dis[l].second, 1);
rub[++top] = dis[l].second;
++l;
}
ans += flg * c.query(L - dis[r].second);
}
while(top) c.update(rub[top--], -1);
}
void dfz(int u){
vis[u] = 1;
cal(u, 0, 0, 1);
for(auto &e : G[u]){
int v = e.second, w = e.first;
if(vis[v]) continue;
cal(v, 1, w, -1);
tn = siz[v], rmn = inf, getRt(v, u);
dfz(rt);
}
vis[u] = 0;
}
int main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> L >> W;
for(int i = 2; i <= n; ++i){
int u, w; cin >> u >> w;
G[i].pb({w, u}), G[u].pb({w, i});
}
tn = n, rmn = inf, getRt(1, 0);
dfz(rt);
ans -= n, ans >>= 1;
cout << ans << endl;
return 0;
}