洛谷 – P4178 – Tree
https://www.luogu.org/problem/P4178
给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 4e4 + 10, mod = 1e9 + 7, inf = 0x3f3f3f3f;
bool vis[maxn];
int sz[maxn], dis[maxn], cnt, cur, sum, zx, k;
ll ans = 0;
vector<pii> g[maxn];
void cal_sz(int u, int p)
{
sz[u] = 1;
for (pii v : g[u]){
if (vis[v.first] || v.first == p) continue;
cal_sz(v.first, u);
sz[u] += sz[v.first];
}
}
int get_zx(int u, int p)//获得当前子树的重心
{
int res = 0;
for (pii v : g[u]){
if (vis[v.first] || v.first == p) continue;
get_zx(v.first, u);
res = max(res, sz[v.first]);
}
res = max(res, sum - sz[u]);
if (res < cur) {
zx = u;
cur = res;
}
return zx;
}
void get_len(int u, int p, int d)//求所有点到这个点的路径长度
{
dis[++cnt] = d;
for (pii v : g[u]){
if (vis[v.first] || v.first == p) continue;
get_len(v.first, u, d + v.second);
}
}
int cal(int u, int d)//尺取计算当前的答案
{
cnt = 0;
get_len(u, 0, d);
sort(dis + 1, dis + 1 + cnt);
int tc = 0, l = 1, r = cnt;
while (l <= r){
if (dis[l] + dis[r] <= k){
tc += r - l;
++l;
}else --r;
}
return tc;
}
void solve(int u)//容斥并分治递归求解
{
vis[u] = 1;
ans += cal(u, 0);
for (pii v : g[u]){
if (vis[v.first]) continue;
ans -= cal(v.first, v.second);//容斥,减去到子节点距离小于等于k - v.second的数量
cal_sz(v.first, 0);
sum = sz[v.first], cur = inf;
int zx = get_zx(v.first, 0);
solve(zx);
}
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0);
int n;
cin >> n;
for (int i = 1; i < n; ++i){
int u, v, w;
cin >> u >> v >> w;
g[u].push_back(pii(v, w));
g[v].push_back(pii(u, w));
}
cin >> k;
cal_sz(1, 0);
sum = n, cur = inf;
solve(get_zx(1, 0));
cout << ans << endl;
return 0;
}
洛谷 – P3806 -【模板】点分治1
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 1e4 + 10, mod = 1e9 + 7, inf = 0x3f3f3f3f;
bool vis[maxn];
int sz[maxn], cnt, cur, sum, zx, k;
ll ans = 0;
vector<pii> g[maxn];
pii dis[maxn];
void cal_sz(int u, int p)
{
sz[u] = 1;
for (pii v : g[u]){
if (vis[v.first] || v.first == p) continue;
cal_sz(v.first, u);
sz[u] += sz[v.first];
}
}
int get_zx(int u, int p)//获得当前子树的重心
{
int res = 0;
for (pii v : g[u]){
if (vis[v.first] || v.first == p) continue;
get_zx(v.first, u);
res = max(res, sz[v.first]);
}
res = max(res, sum - sz[u]);
if (res < cur) {
zx = u;
cur = res;
}
return zx;
}
void get_len(int son, int u, int p, int d)//求所有点到这个点的路径长度
{
dis[++cnt] = pii(d, son);
for (pii v : g[u]){
if (vis[v.first] || v.first == p) continue;
get_len(son, v.first, u, d + v.second);
}
}
bool cal(int u, int d)//尺取计算当前的答案
{
cnt = 0;
dis[++cnt] = pii(0, 0);
for (pii v : g[u]) if (!vis[v.first]) get_len(v.first, v.first, u, v.second);
sort(dis + 1, dis + 1 + cnt);
int l = 1, r = cnt;
while (l <= r){
if (dis[l].first > k) break;
int sr = upper_bound(dis + 1 + l, dis + 1 + r, pii(k - dis[l].first, inf)) - dis;
int sl = lower_bound(dis + 1 + l, dis + 1 + r, pii(k - dis[l].first, 0)) - dis;
if (sl < sr && l == 1) return true;
for (int i = sl; i < sr; ++i){
if (dis[i].second != dis[l].second || dis[l].first == 0) return true;
}
++l;
}
return false;
}
bool solve(int u)//分治递归求解
{
vis[u] = 1;
if (cal(u, 0)) return true;
for (pii v : g[u]){
if (vis[v.first]) continue;
cal_sz(v.first, 0);
sum = sz[v.first], cur = inf;
int zx = get_zx(v.first, 0);
if (solve(zx)) return true;
}
return false;
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i < n; ++i){
int u, v, w;
cin >> u >> v >> w;
g[u].push_back(pii(v, w));
g[v].push_back(pii(u, w));
}
while (m--){
cin >> k;
cal_sz(1, 0);
sum = n, cur = inf;
if (solve(get_zx(1, 0))) cout << "AYE" << '\n';
else cout << "NAY" << '\n';
ans = 0;
memset(vis, 0, sizeof(vis));
}
return 0;
}