dfs序 题目小集

来源:互联网 发布:淘宝卷和商品连在一起 编辑:程序博客网 时间:2024/05/16 02:54

参考

dfs序题目练习 ——樱花庄的龙之介大人
dfs序专题学习 ——Miracle_ma

HDU 5692 +线段树

题意

给定一棵树,有两种操作:
1. 改变某个点 x 的权值;
2. 定义路径的价值为其上所有点的权值之和,询问以 x 为根的子树内的点到根的路径的价值的最大值。

分析

(画好了图上传不了就很气)

        1        /\    2      3    /\4       5

对于这样的一棵树,dfs一遍,记录下每个节点的时间戳以及以它为根的子树中时间戳最大的节点的时间戳,即

le[u]=cnt[u],ri[u]=max{cnt[v]|vu}

就上图而论,

id le ri 1 1 5 2 2 4 3 5 5 4 3 3 5 4 4

将原树映射到一棵线段树上,线段树的线段为dfs后每个节点的新编号(即时间戳,亦即le[]),维护的是该节点到根的路径的价值(比如说上例:[1,1]维护节点1到根的价值,[2,2]维护节点2到根的价值,[3,3]维护节点4到根的价值,[4,4]维护节点5到根的价值,[5,5]维护节点3到根的价值)。
而对点 x 的修改以及查询操作即为对区间[le[x],ri[x]]的修改与查询。

这么好用的嘛!

是的,我们考虑题目的要求:改变某个点 x 的权值;查询以 x 为根的子树内的点到根的路径的价值的最大值。
更改点 x 的权值 影响的 即 以 x 为根的子树内所有的点到根的路径的价值,所以 [le[x],ri[x]] 一整段都需要修改,查询的话即查询 [le[x],ri[x]] 一段的最大值。

希望讲清楚了QWQ

Code

#pragma comment(linker, "/STACK:1024000000,1024000000")#include <bits/stdc++.h>#define lson (rt << 1)#define rson (rt << 1 | 1)#define maxn 100010typedef long long LL;int le[maxn], ri[maxn], ne[maxn], tot, cnt, kas;LL val[maxn], p[maxn], a[maxn];struct node {    int l, r; LL val, tag;}tr[maxn * 4];struct Edge {    int to, ne;    Edge(int a = 0, int b = 0): to(a), ne(b) {}}edge[maxn * 2];void add(int u, int v) {    edge[tot] = Edge(v, ne[u]);    ne[u] = tot++;}void init() {    tot = cnt = 0;    memset(ne, -1, sizeof(ne));}inline LL max(LL a, LL b) { return a > b ? a : b; }inline int midi(int l, int r) { return l + r >> 1; }inline void push_up(int rt) { tr[rt].val = max(tr[lson].val, tr[rson].val); }inline void push_down(int rt) {    if (tr[rt].tag) {        tr[lson].tag += tr[rt].tag; tr[rson].tag += tr[rt].tag;        tr[lson].val += tr[rt].tag; tr[rson].val += tr[rt].tag;        tr[rt].tag = 0;    }}void build(int rt, int l, int r) {    tr[rt].l = l; tr[rt].r = r; tr[rt].tag = 0;    if (l == r) { tr[rt].val = a[l]; return; }    int mid = midi(l, r);    build(lson, l, mid); build(rson, mid + 1, r);    push_up(rt);}void dfs(int u, int fa, LL w) {    le[u] = ri[u] = ++cnt;    p[u] = w;    for (int i = ne[u]; i != -1; i = edge[i].ne) {        int v = edge[i].to;        if (v == fa) continue;        dfs(v, u, w + val[v]);        ri[u] = max(ri[u], ri[v]);    }}void modify(int rt, int l, int r, LL add) {    if (tr[rt].l == l && tr[rt].r == r) {        tr[rt].tag += add;        tr[rt].val += add;        return;    }    push_down(rt);    int mid = midi(tr[rt].l, tr[rt].r);    if (r <= mid) modify(lson, l, r, add);    else if (l > mid) modify(rson, l, r, add);    else { modify(lson, l, mid, add); modify(rson, mid + 1, r, add); }    push_up(rt);}LL query(int rt, int l, int r) {    if (tr[rt].l == l && tr[rt].r == r) return tr[rt].val;    push_down(rt);    int mid = midi(tr[rt].l, tr[rt].r);    if (r <= mid) return query(lson, l, r);    else if (l > mid) return query(rson, l, r);    else return max(query(lson, l, mid), query(rson, mid + 1, r));}void work() {    printf("Case #%d:\n", ++kas);    init();    int n, m;    scanf("%d%d", &n, &m);    for (int i = 1; i < n; ++i) {        int u, v;        scanf("%d%d", &u, &v);        add(u, v); add(v, u);    }    for (int i = 0; i < n; ++i) scanf("%lld", &val[i]);    dfs(0, -1, val[0]);    for (int i = 0; i < n; ++i) a[le[i]] = p[i];    build(1, 1, n);    while (m--) {        int x, u; LL w;        scanf("%d", &x);        if (x == 0) {            scanf("%d%lld", &u, &w);            modify(1, le[u], ri[u], w - val[u]);            val[u] = w;        }        else {            scanf("%d", &u);            printf("%lld\n", query(1, le[u], ri[u]));        }    }}int main() {    freopen("in.txt", "r", stdin);    int T;    scanf("%d", &T);    while (T--) work();    return 0;}

POJ 3321 +树状数组

麻烦移步本菜另一篇博文 poj 3321 Apple Tree 树状数组 dfs序

HDU 5468 +莫比乌斯反演

题意

给定一棵树,每个节点都有一个权值。对于每一个节点,问以它为根的子树中的点与它的权值互质的有多少个。

参考

AOQNRMGYXLMV 的博客

分析

即问

i=lr[(vall,vali)=1]

d|vallμ(d)i=lr[d|vali]

cnt[] 数组记录每个因子出现的个数,对于点 u,比较回溯时的值和第一次访问时 cnt[] 数组的差值即可。

Code

#include <bits/stdc++.h>#include <vector>#define maxn 100000#define maxm maxn + 10using namespace std;int mu[maxm], prime[maxm], cnt[maxm], kas, ans[maxm], ne[maxm], tot, val[maxm];bool check[maxm];vector<int> fac[maxm];struct Edge {    int to, ne;    Edge(int a = 0, int b = 0) : to(a), ne(b) {}}edge[maxn * 2];void add(int u, int v) {    edge[tot] = Edge(v, ne[u]);    ne[u] = tot++;}void init() {    int tot = 0; mu[1] = 1;    for (int i = 1; i <= maxn; ++i) fac[i].push_back(1);    for (int i = 2; i <= maxn; ++i) {        if (!check[i]) {            prime[tot++] = i;            mu[i] = -1;        }        for (int j = 0; j < tot; ++j) {            if (i * prime[j] > maxn) break;            check[i * prime[j]] = true;            if (i % prime[j] == 0) {                mu[i * prime[j]] = 0;                break;            }            mu[i * prime[j]] = -mu[i];        }        if (mu[i] != 0) {            for (int j = i; j <= maxn; j += i) fac[j].push_back(i);        }    }}void dfs(int u, int fa) {    vector<int> pre;    ans[u] = 0;    for (auto x : fac[val[u]]) {        pre.push_back(cnt[x]);        ++cnt[x];    }    for (int i = ne[u]; ~i; i = edge[i].ne) {        int v = edge[i].to;        if (v == fa) continue;        dfs(v, u);    }    vector<int>& ve = fac[val[u]];    int sz = ve.size();    for (int i = 0; i < sz; ++i) {        ans[u] += mu[ve[i]] * (cnt[ve[i]] - pre[i]);    }}int n;void work() {    memset(cnt, 0, sizeof(cnt));    memset(ne, -1, sizeof(ne));    tot = 0;    for (int i = 1; i < n; ++i) {        int u, v;        scanf("%d%d", &u, &v);        add(u, v); add(v, u);    }    for (int i = 1; i <= n; ++i) scanf("%d", &val[i]);    dfs(1, -1);    printf("Case #%d: %d", ++kas, ans[1]);    for (int i = 2; i <= n; ++i) printf(" %d", ans[i]); printf("\n");}int main() {    freopen("in.txt", "r", stdin);    init();    while (scanf("%d", &n) != EOF) work();    return 0;}

HDU 3887 +树状数组

题意

给定一棵树,对每个节点 if(i)f(i) 为以 i 为根的子树中的编号 <i 的点的数量。

思路

求得 dfs序后,维护一个树状数组,从大到小查询 f(i),查完一个扔一个,保证树状数组中存在的数都小于当前查询的数 i,这样 [le[i],ri[i]] 内存在的数必定也都小于 i.

(哇这种从大到小查询啦从小到大插入啦的思想真的炒鸡重要QWQ!!!

Code

#include <bits/stdc++.h>#define maxn 100010using namespace std;struct Edge {    int to, ne;    Edge(int a = 0, int b = 0) : to(a), ne(b) {}}edge[maxn * 2];int ans[maxn], le[maxn], ri[maxn], c[maxn], cnt, tot, ne[maxn];void addEdge(int u, int v) {    edge[tot] = Edge(v, ne[u]);    ne[u] = tot++;}void dfs(int u, int fa) {    le[u] = ri[u] = ++cnt;    for (int i = ne[u]; ~i; i = edge[i].ne) {        int v = edge[i].to;        if (v == fa) continue;        dfs(v, u);        ri[u] = max(ri[u], ri[v]);    }}int n, p;inline int lowbit(int x) { return x & (-x); }inline void add(int x, int del) { while (x <= n) c[x] += del, x += lowbit(x); }inline int query(int x) { int ret = 0; while (x) ret += c[x], x -= lowbit(x); return ret; }void work() {    tot = cnt = 0;    memset(ne, -1, sizeof(ne));    memset(c, 0, sizeof(c));    for (int i = 1; i < n; ++i) {        int u, v;        scanf("%d%d", &u, &v);        addEdge(u, v); addEdge(v, u);    }    dfs(p, -1);    for (int i = 1; i <= n; ++i) add(i, 1);    for (int i = n; i >= 1; --i) {        ans[i] = query(ri[i]) - query(le[i]);        add(le[i], -1);    }    printf("%d", ans[1]);    for (int i = 2; i <= n; ++i) printf(" %d", ans[i]); printf("\n");}int main() {    while (scanf("%d%d", &n, &p) != EOF && n + p) work();    return 0;}

update-2017.9.5

今天写 HDU 5877 Weak Pair dfs序 + 树状数组 + 离散化 时又回来看了看这里,觉得当时怕不是智障了...
就和上面那个莫比乌斯的题一样,进去的时候算一次,出来的时候算一次,就解决了啊0.0
当时愣是不会做去百度然后看到 从大到小查完一个扔一个 的思路觉得棒呆了…(虽然的确是很重要的思想),但这道题本身是很直白很简单的思路…

于是又写了一发

Code

#include <bits/stdc++.h>#define maxn 100010int c[maxn], ans[maxn], n, p, ne[maxn], tot;struct Edge {    int to, ne;    Edge(int a = 0, int b = 0) : to(a), ne(b) {}}edge[maxn * 2];void addEdge(int u, int v) {    edge[tot] = Edge(v, ne[u]);    ne[u] = tot++;}int lowbit(int x) { return x & -x; }void add(int x, int d) { while (x <= n) c[x] += d, x += lowbit(x); }int query(int x) { int ret = 0; while (x) ret += c[x], x -= lowbit(x); return ret; }void dfs(int u, int fa) {    add(u, 1);    int temp = query(u);    for (int i = ne[u]; ~i; i = edge[i].ne) {        int v = edge[i].to;        if (v == fa) continue;        dfs(v, u);    }    ans[u] = query(u) - temp;}void work() {    tot = 0;    memset(c, 0, sizeof c);    memset(ne, -1, sizeof ne);    for (int i = 1; i < n; ++i) {        int u, v;        scanf("%d%d", &u, &v);        addEdge(u, v); addEdge(v, u);    }    dfs(p, -1);    printf("%d", ans[1]);    for (int i = 2; i <= n; ++i) printf(" %d", ans[i]); printf("\n");}int main() {    while (scanf("%d%d", &n, &p) != EOF && n + p) work();    return 0;}

CF 620E +线段树+bitmasks

题意

给定一棵树,每个节点都有涂有一种颜色(颜色总数 60)。现有两种操作:
1. 将以 x 为根的子树全都涂成 c 色;
2. 询问以 x 为根的子树中有多少种颜色。

思路

十分裸的 dfs序 + 线段树。
(这种涂色问颜色数的本菜之前也在 线段树 lazy tag 小合集 -POJ2777写到过,虽然这次和当年(雾)做法并不一样)
这次看了 Tutorial 于是学习到了一发 bitmasks,颜色数只有 60,于是可以用 long long来表示,区间求颜色总数在这里就是求个或。
一个注意点:左移时要用 1LL << c

Code

#include <bits/stdc++.h>#define maxn 400010#define lson (rt << 1)#define rson (rt << 1 | 1)using namespace std;typedef long long LL;int col[maxn], c[maxn], le[maxn], ri[maxn], tot, cnt, ne[maxn];struct Edge {    int to, ne;    Edge(int a = 0, int b = 0): to(a), ne(b) {}}edge[maxn * 2];struct node {    int l, r; LL tag, mask;}tr[maxn * 4];inline int midi(int l, int r) { return l + r >> 1; }void add(int u, int v) {    edge[tot] = Edge(v, ne[u]);    ne[u] = tot++;}void dfs(int u, int fa) {    le[u] = ri[u] = ++cnt;    for (int i = ne[u]; ~i; i = edge[i].ne) {        int v = edge[i].to;        if (v == fa) continue;        dfs(v, u);        ri[u] = max(ri[u], ri[v]);    }}inline void push_up(int rt) {    tr[rt].mask = tr[lson].mask | tr[rson].mask;}inline void push_down(int rt) {    if (tr[rt].tag) {        tr[lson].mask = tr[rson].mask = tr[lson].tag = tr[rson].tag = tr[rt].tag;        tr[rt].tag = 0;    }}int count(LL x) {    int ret = 0;    while (x) {        ++ret;        x &= (x - 1);    }    return ret;}void build(int rt, int l, int r) {    tr[rt].l = l, tr[rt].r = r, tr[rt].tag = 0;    if (l == r) { tr[rt].mask = (1LL << c[l]); return; }    push_up(rt);    int mid = midi(l, r);    build(lson, l, mid); build(rson, mid +1, r);    push_up(rt);}void modify(int rt, int l, int r, int c) {    if (tr[rt].l == l && tr[rt].r == r) {        tr[rt].mask = (1LL << c);        tr[rt].tag = (1LL << c);        return;    }    push_down(rt);    int mid = midi(tr[rt].l, tr[rt].r);    if (r <= mid) modify(lson, l, r, c);    else if (l > mid) modify(rson, l, r, c);    else { modify(lson, l, mid, c); modify(rson, mid + 1, r, c); }    push_up(rt);}LL query(int rt, int l, int r) {    if (count(tr[rt].mask) == 1) return tr[rt].mask;    if (tr[rt].l == l && tr[rt].r == r) return tr[rt].mask;    push_down(rt);    int mid = midi(tr[rt].l, tr[rt].r);    if (r <= mid) return query(lson, l, r);    else if (l > mid) return query(rson, l, r);    else return query(lson, l, mid) | query(rson, mid + 1, r);    push_up(rt);}int n, m;void work() {    tot = cnt = 0; memset(ne, -1, sizeof(ne));    for (int i = 1; i <= n; ++i) scanf("%d", &col[i]);    for (int i = 1; i < n; ++i) {        int u, v;        scanf("%d%d", &u, &v);        add(u, v); add(v, u);    }    dfs(1, -1);    for (int i = 1; i <= n; ++i) c[le[i]] = col[i] - 1;    build(1, 1, n);    while (m--) {        int t, v, c;        scanf("%d", &t);        if (t == 1) {            scanf("%d%d", &v, &c);            modify(1, le[v], ri[v], c - 1);        }        else {            scanf("%d", &v);            printf("%d\n", count(query(1, le[v], ri[v])));        }    }}int main() {    while (scanf("%d%d", &n, &m) != EOF) work();    return 0;}
原创粉丝点击