【算法练习】Luogu P4149 [IOI2011]Race(点分治)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/pengwill97/article/details/82563088

题意

给一棵树,每条边有权。求一条简单路径,权值和等于 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;
}

猜你喜欢

转载自blog.csdn.net/pengwill97/article/details/82563088
今日推荐