點分治
例題
ciel the commander
題解:
讓你給一個樹每個點用小寫字母標號, 要求每兩個相同的標號之間的路徑上至少有一個標號比他們大的點。
甚至算不上點分治,但是可以練習一下點分的求重心。
考慮貪心,每次找一個點標當前最高的號,然後一棵樹分成了幾棵子樹,子樹之間的路徑肯定會經過當前標號的點,就不需要考慮了,那麼遞歸下去就好了。
然後假如每次對於一棵子樹選他的重心標號,最多只需要個標號就好了。
Tree
來源:poj 1741
題解:
一棵樹,求有多少組點對,距離不超過k。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
void chkMax(int &x, int y){if (x < y) x = y;}
void chkMin(int &x, int y){if (x > y) x = y;}
const int N = 1e4+10;
const int M = N<<1;
const int inf = 1e9+7;
struct G{
int h[N], e, nxt[M], v[M], w[M];
void clear(){
memset(h, 0, sizeof(h));
e = 1;
}
void add_dir(int _u, int _v, int _w){
++ e;
nxt[e] = h[_u];
v[e] = _v;
w[e] = _w;
h[_u] = e;
}
void add_undir(int _u, int _v, int _w){
add_dir(_u, _v, _w);
add_dir(_v, _u, _w);
}
}g;
int n, k, ans;
int sz[N], zx, mx, siz;
int l, r, q[N], dpt[N];
bool vis[N];
void dfs2(int u, int fa)
{
q[++ r] = dpt[u];
for (int i = g.h[u]; i; i = g.nxt[i]){
int v = g.v[i];
int w = g.w[i];
if (v == fa || vis[v]) continue;
dpt[v] = dpt[u]+w;
dfs2(v, u);
}
}
int calc(int u, int val)
{
int ret = 0;
l = 1; r = 0; dpt[u] = val;
dfs2(u, 0);
sort(q+1, q+r+1);
while (l < r){
if (q[l]+q[r] <= k) ret += r-l, ++ l;
else -- r;
}
return ret;
}
void get_zx(int u, int fa)
{
sz[u] = 1;
int tmp = 0;
for (int i = g.h[u]; i; i = g.nxt[i]){
int v = g.v[i];
if (v == fa || vis[v]) continue;
get_zx(v, u);
chkMax(tmp, sz[v]);
sz[u] += sz[v];
}
chkMax(tmp, siz-sz[u]);
if (tmp < mx) mx = tmp, zx = u;
}
void dfs1(int u)
{
vis[u] = 1;
ans += calc(u, 0);
for (int i = g.h[u]; i; i = g.nxt[i]){
int v = g.v[i];
int w = g.w[i];
if (vis[v]) continue;
siz = sz[v]; mx = inf;
ans -= calc(v, w);
get_zx(v, u);
dfs1(zx);
}
}
int main()
{
while(scanf("%d%d", &n, &k) && n && k){
g.clear();
ans = 0;
for (int i = 1; i < n; ++ i){
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
g.add_undir(x, y, z);
}
memset(vis, 0, sizeof(vis));
siz = n; mx = inf;
get_zx(1, 0);
dfs1(zx);
printf("%d\n", ans);
}
return 0;
}
最短路徑樹問題
來源:bzoj4016
重建計劃
動態點分治
例題
[ZJOI2007]捉迷藏
板子題。
題解鏈接在這裏:my solution