题意
给一棵树,每条边有权。求一条简单路径,权值和等于 K,且边的数量最小。
题解
点分治
代码
方法1
先遍历根节点,然后去除路径节点全在子树的方法。常数稍微大一点。
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
const int nmax = 2e5 + 7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const ull p = 67;
const ull MOD = 1610612741;
struct edge {
int to, nxt, w;
}e[nmax<<1];
int head[nmax], tot;
void add_edge(int u ,int v, int w) {
e[tot].to = v;
e[tot].nxt = head[u];
e[tot].w = w;
head[u] = tot++;
}
int n, k;
int sz[nmax], root, nowmx, totnode;
bool visit[nmax];
int num[(int) 1e6 + 7];
//int num = 0, mne;
void getroot(int u, int f) {
sz[u] = 1;
int mxpart = 0;
for(int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if(v != f && !visit[v]) {
getroot(v, u);
sz[u] += sz[v];
mxpart = max(mxpart, sz[v]);
}
}
mxpart = max(mxpart, totnode - sz[u]);
if(mxpart < nowmx) {
nowmx = mxpart;
root = u;
}
}
struct node {
int d, t;
bool operator < (const node & rhs) const {
if(d == rhs.d) return t < rhs.t;
else return d < rhs.d;
}
}ss[nmax];
int top;
void dfs(int u, int f, int t, int d) {
if(d > k)
return;
ss[++top].d = d;
ss[top].t = t;
for(int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if(v != f && !visit[v]) {
dfs(v, u, t + 1, d + e[i].w);
}
}
}
//inline void update(int tmp, int tag) {
// if(tag == 1) {
// if(tmp < mne) {
// mne = tmp;
// num = 1;
printf("in\n");
// } else if(tmp == mne) {
// num ++;
printf("in\n");
// }
// } else {
// if(tmp == mne) {
// num --;
// if(num == 0) {
// mne = INF;
// }
// }
// }
//}
void getans(int u,int inittime, int initdis, int tag) {
top = 0;
dfs(u, u, inittime, initdis);
sort(ss + 1, ss + 1 + top);
int l = 1, r = top;
while(l <= r) {
while (l < r && ss[l].d + ss[r].d > k) r --;
for (int i = r; ss[l].d + ss[i].d == k; i--)
num[ss[l].t + ss[i].t] += tag;
l++;
// else if(ss[l].d + ss[r].d < k) l ++;
// else {
// if(ss[l].d == ss[r].d) {
// int tmp = ss[l].t + ss[l + 1].t;
mne = min(mne, tmp);
// update(tmp, tag);
// break;
// } else {
// int i = l, j = r;
// while(ss[l].d == ss[i].d) ++i;
// while(ss[r].d == ss[j].d) --j;
// int tmp = ss[l].t + ss[j+1].t;
mne = min(mne, tmp);
// update(tmp, tag);
// l = i, r = j;
// }
// }
}
}
void solve(int u) {
getans(u, 0 ,0, 1);
visit[u] = true;
for(int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if(!visit[v]) {
getans(v, 1, e[i].w, -1);
root = 0, nowmx = INF, totnode = sz[v];
getroot(v, u);
solve(root);
}
}
}
int main(){
scanf("%d %d", &n, &k);
memset(head, -1, sizeof head);
tot = 0;
for(int i = 2; i <= n; ++i) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
u ++;
v ++;
add_edge(u, v, w);
add_edge(v, u, w);
}
root = 0, nowmx = INF, totnode = n;
// num = 0, mne = INF;
getroot(1, -1);
solve(root);
int finalans = 0;
for(int i = 1; i <= n; ++i) {
if(num[i] != 0) {
finalans = i;
break;
}
}
if(finalans == 0) {
// assert(num == 0);
printf("-1\n");
} else printf("%d\n", finalans);
return 0;
}
方法2
依次遍历子树,距离的最小边数目。常数更小。
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
const int nmax = 1e6+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const ull p = 67;
const ull MOD = 1610612741;
struct edge {
int to, nxt, w;
}e[nmax<<1];
int head[nmax], tot;
int n, k;
int sz[nmax], root, nowmin, totnode, ans;
int tmpdis[nmax], dist[nmax], lenmin[nmax];
bool visit[nmax];
void add_edge(int u, int v, int w) {
e[tot].to = v;
e[tot].nxt = head[u];
e[tot].w = w;
head[u] = tot++;
}
void getroot(int u, int f) {
sz[u] = 1;
int mxchild = 0;
for(int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if(!visit[v] && v != f) {
getroot(v, u);
sz[u] += sz[v];
mxchild = max(sz[v], mxchild);
}
}
mxchild = max(mxchild, totnode - sz[u]);
if(mxchild < nowmin) {
nowmin = mxchild;
root = u;
}
}
void update(int u, int f,int dis, int times) {
if(dis <= k) {
lenmin[dis] = min(lenmin[dis], times);
} else return;
for(int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if(v != f && !visit[v]) {
update(v, u, dis + e[i].w, times + 1);
}
}
}
void getans(int u, int f, int dis, int times) {
if(dis <= k) ans = min(ans, times + lenmin[k - dis]);
else return;
// if(times + lenmin[k - dis] < ans)
// ans = times + lenmin[k - dis];
for(int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if(v != f && !visit[v]) {
getans(v, u, dis + e[i].w, times + 1);
}
}
}
void recover(int u, int f, int dis) {
if(dis <= k) lenmin[dis] = INF;
else return;
for(int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if(v != f && !visit[v]) {
recover(v, u, dis + e[i].w);
}
}
}
void solve(int u) {
visit[u] = true;
lenmin[0] = 0;
for(int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if(!visit[v]) {
getans(v, u, e[i].w, 1);
update(v, u, e[i].w, 1);
}
}
for(int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if(!visit[v]) {
recover(v, u, e[i].w);
}
}
for(int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if(!visit[v]) {
root = 0, nowmin = INF, totnode = sz[v];
getroot(v, u);
solve(root);
}
}
}
int main() {
scanf("%d %d", &n, &k);
int u, v, w;
ans = INF;
memset(head, -1, sizeof head);
for(int i = 1; i <= k; ++i)
lenmin[i] = INF;
for(int i = 2; i <= n; ++i) {
scanf("%d %d %d", &u, &v, &w);
u ++;
v ++;
add_edge(u, v, w);
add_edge(v, u, w);
}
root = 0, nowmin = INF, totnode = n;
getroot(1, -1);
solve(root);
if(ans == INF) printf("-1\n");
else printf("%d\n", ans);
return 0;
}