hdu3974 Assign the task(线段树/dfs)(好题)

来源:互联网 发布:手机预算软件 编辑:程序博客网 时间:2024/05/18 11:46

题目链接:点击链接

题意:

给一棵n个点的树,点标号从1到n。有两种操作,操作1,将树上的某个点以及它的子孙都赋值为某个数;操作2,查询某个点的值。

思路:

做法一:

dfs。更新时,将目标节点更新就行,给节点记录一个时间戳。查询时,向上访问父亲节点,对比儿子节点和父亲节点的时间戳,如果父亲节点的时间戳晚于儿子节点,儿子的值等于父亲的值,反之亦然。

代码:

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int maxn = 50005;int cnt, cnt_now, ans;int ord[maxn], fa[maxn], idx[maxn];void init(){    memset(ord, -1, sizeof(ord));    memset(idx, -1, sizeof(idx));    memset(fa, -1, sizeof(fa));    cnt = 0;}void dfs(int x){    if(fa[x]==-1){        if(ord[x] > cnt_now){            cnt_now = ord[x];            ans = idx[x];        }    }    else{        if(ord[x] < ord[fa[x]]){            ord[x] = ord[fa[x]];            idx[x] = idx[fa[x]];        }        if(ord[x] > cnt_now){            cnt_now = ord[x];            ans = idx[x];        }        dfs(fa[x]);    }}int main(){    char opt[10];    int t, n, a, b, m, Case=0;    scanf("%d", &t);    while(t--){        init();        scanf("%d", &n);        for(int i=0; i<n-1; ++i){            scanf("%d%d", &a, &b);            fa[a] = b;        }        scanf("%d", &m);        printf("Case #%d:\n", ++Case);        for(int i=0; i<m; ++i){            scanf("%s", opt);            if(opt[0] == 'C'){                scanf("%d", &a);                cnt_now = -1; ans = -1;                dfs(a);                printf("%d\n", ans);            }            if(opt[0] == 'T'){                scanf("%d%d", &a, &b);                ord[a] = ++cnt;                idx[a] = b;            }        }    }    return 0;}

做法二:

用dfs序建线段树。思路相当于,把当前节点管理所有儿子节点,转换为当前节点对应一条包含所有儿子的线段,

如何转换呢?dfs的时候,访问到当前节点x后,给节点x一个在线段树中的下标cnt,即节点x在线段树中对应的起点,记录为L[x] =cnt,继续深入,重复之前的操作,cnt不断加1。dfs再次返回到x时,我们知道节点x所管理的儿子在线段树中的终点为cnt,记录为R[x] = cnt,那么x节点对应的线段在线段树中为[L[x], R[x]],这样就完成了树到线段树的映射。

之后就是对应的区间懒惰更新,单点查询。

代码:

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <vector>using namespace std;const int maxn = 50005;int cnt;int L[maxn], R[maxn];int tree[maxn<<2];int lazy[maxn<<2];int pre[maxn];vector<int> edges[maxn];void init(int n){    cnt = 0;    memset(pre, 0, sizeof(pre));    memset(tree, -1, sizeof(tree));    memset(lazy, -1, sizeof(tree));    for(int i=1; i<=n; ++i) edges[i].clear(); }void dfs(int rt){    L[rt] = ++cnt;    for(int i=0; i<edges[rt].size(); ++i){        dfs(edges[rt][i]);    }    R[rt] = cnt;}void pushdown(int rt){    if(lazy[rt]!=-1){        tree[rt<<1] = tree[rt<<1|1] = tree[rt];        lazy[rt<<1] = lazy[rt<<1|1] = lazy[rt];        lazy[rt] = -1;     }}void update(int rt, int a, int b, int l, int r, int x){    if(a<=l && r<=b){        tree[rt] = lazy[rt] = x;    }    else{        pushdown(rt);        int mid = (l+r)>>1;        if(a<=mid) update(rt<<1, a, b, l, mid, x);        if(mid<b) update(rt<<1|1, a, b, mid+1, r, x);    }}int query(int rt, int idx, int l, int r){    if(l==r && l==idx){        return tree[rt];    }    else{        int mid = (l+r)>>1;        pushdown(rt);        if(idx<=mid) return query(rt<<1, idx, l, mid);        if(mid<idx) return query(rt<<1|1, idx, mid+1, r);    }}int main(){    char opt[10];    int t, n, a, b, m, Case=0;    scanf("%d", &t);    while(t--){        scanf("%d", &n);        init(n);        for(int i=0; i<n-1; ++i){            scanf("%d%d", &a, &b);            edges[b].push_back(a);            ++pre[a];        }        for(int i=1; i<=n; ++i){            if(pre[i]==0){                dfs(i);                break;            }        }        scanf("%d", &m);        printf("Case #%d:\n", ++Case);        for(int i=0; i<m; ++i){            scanf("%s", opt);            if(opt[0] == 'C'){                scanf("%d", &a);                printf("%d\n", query(1, L[a], 1, cnt));            }            if(opt[0] == 'T'){                scanf("%d%d", &a, &b);                update(1, L[a], R[a], 1, cnt, b);            }        }    }    return 0;}
原创粉丝点击