[贪心+DFS序列维护树上前缀和]2014 Multi-University Training Contest 5 - 1002 Paths on the tree

来源:互联网 发布:windows 7模拟器手机版 编辑:程序博客网 时间:2024/06/06 18:17

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4912

题目大意:给定一颗树和树上的m条路径,询问最多取多少两两不相交的路径

思路:若两路径相交,一定存在公共的LCA,比赛时考虑匹配,DP之类的思路,没有结果,官方解法是很巧妙的贪心:

            把所有路径的LCA按深度从大到小排序,按这个顺序取所有互不相交的路径数就是答案。

            若取了一条路径,则过与这条路径共LCA的路径都不能取。

            按深度从大往小取,可以保证之前放好的路径不会影响到后面的路径,且每次留给后面的路径数最多。

            具体实现先按LCA的深度排序,取路径时树状数组维护DFS序列。

            若取一条路径,把DFS序中LCA所在的st[i]维护+1,出了LCA的节点en[i]+1维护-1,表示LCA所在的子树已经不能取路径(深度比该LCA大的路径已经取完,深度比LCA小的路径不能经过这颗子树)

            判断能否取一条路径,计算路径两端点到LCA的前缀和是否为0(sum(st[A / B])- sum(st[ father[LCA] ])),为0表示路径不存在取过路径的子树


代码:

#include <cstdio>#include <iostream>#include <algorithm>#include <cstring>#include <cmath>#include <queue>#include <map>#include <set>using namespace std;#define foru(i, a, b) for(int i=(a); i<=(b); i++)#define ford(i, a, b) for(int i=(a); i>=(b); i--)#define clr(a, b) memset(a, (b), sizeof(a))typedef long long ll;const double Pi = 4 * atan(1.0);inline int readint() {    char c = getchar();    while(!isdigit(c)) c = getchar();    int ret = 0;    while(isdigit(c)) ret = ret * 10 + c - '0', c = getchar();    return ret;}/*************************************************************/const int N = 100010;struct Edge{    int a, b;    int lca;}E[N];int n, m, tot, tim;int adj[N], next[2*N], aim[2*N], dep[N], fa[N];int anc[N][21], st[N], en[N], c[N];void add(int a, int b){    tot ++;    aim[tot] = b;    next[tot] = adj[a];    adj[a] = tot;}void init(){    tot = 0; clr(adj, 0);    foru(i, 1, n-1){        int u = readint();        int v = readint();        add(u, v);        add(v, u);    }    foru(i, 1, m) {        E[i].a = readint();        E[i].b = readint();    }}void dfs(int x, int pre){    st[x] = ++tim;    dep[x] = dep[pre] + 1;    fa[x] = anc[x][0] = pre;    int k = adj[x];    while (k){        int tx = aim[k];        if (tx != pre) dfs(tx, x);        k = next[k];    }    en[x] = tim;}int swim(int x, int H){    for(int i = 0; H; i++){        if (1&H) x = anc[x][i];        H >>= 1;    }    return x;}int LCA(int x, int y){    if (dep[x] < dep[y]) swap(x, y);    x = swim(x, dep[x] - dep[y]);    if (x == y) return x;    ford(i, 20, 0)        if (anc[x][i] != anc[y][i]){            x = anc[x][i];            y = anc[y][i];        }    return anc[x][0];}bool cmp(Edge x, Edge y){    return dep[x.lca] > dep[y.lca];}void prepare(){    tim = dep[1] = 0; dfs(1, 1); fa[1] = 0;    foru(j, 1, 20) foru(i, 1, n)        anc[i][j] = anc[anc[i][j-1]][j-1];    foru(i, 1, m)        E[i].lca = LCA(E[i].a, E[i].b);    sort(E+1, E+1+m, cmp);}int lowbit(int x){    return x&(-x);}void modify(int k, int x){    while (k < n){        c[k] += x;        k += lowbit(k);    }}int sum(int k){    int s = 0;    while (k > 0){        s += c[k];        k -= lowbit(k);    }    return s;}int ans;void solve(){    ans = 0; clr(c, 0);    foru(i, 1, m){        int a = E[i].a;        int b = E[i].b;        int lca = E[i].lca;        if (sum(st[a]) - sum(st[fa[lca]]) != 0) continue; // ? fa[lca]        if (sum(st[b]) - sum(st[fa[lca]]) != 0) continue;        ans ++;        modify(st[lca], 1);        modify(en[lca]+1, -1);    }    printf("%d\n", ans);}int main(){    freopen("1002.txt", "r", stdin);    while (scanf("%d %d", &n, &m) != EOF){        init();        prepare();        solve();    }    return 0;}

Tips :

DSF序列+树状数组可维护某节点到根的前缀和 -> 子树的st[i]维护+1,en[i]+1维护-1;


0 0
原创粉丝点击