Spoj 375 Qtree 树链剖分 + 线段树 解法

来源:互联网 发布:淘宝鞋子店铺推荐 编辑:程序博客网 时间:2024/05/16 14:09

前面介绍过Qtree的动态树解法,现在写一种更高效的,使用树链剖分,数据结构采用线段树:

题目链接:http://www.spoj.pl/problems/QTREE/

代码:

#include <stdio.h>#include <string.h>#include <queue>#include <vector>using namespace std;#define MAXN 10010#define lx (x<<1)#define rx (x<<1 | 1)#define MID ((l + r)>>1)int A[MAXN];//存储边长int M[MAXN<<2];//建立线段树int T;int N;int static_id;struct ANode{    int v;    int w;    ANode * next;    ANode(int _v,int _w,ANode * _next)    {        v = _v;        w = _w;        next = _next;    }}*adj[MAXN];struct Node{    Node * father;    Node * ch;    int cost;    int deep;    bool vis;    int size;    int top;    int tid;    int id;} tree[MAXN],*null,Tnull;vector<pair<int,int> > edge;void init(Node * p){    p->father = p->ch = null;    p->vis = false;}//生成重链void dfs(int x,int father,int depth){    tree[x].deep = depth;    if(father!=0)    {        tree[x].father = tree + father;    }    tree[x].size = 1;    tree[x].vis = true;    int maxsize = 0;    for(ANode * p = adj[x]; p; p=p->next)    {        if(tree[p->v].vis == false)        {            dfs(p->v,x,depth+1);            tree[p->v].cost = p->w;            tree[x].size += tree[p->v].size;            if(tree[p->v].size > maxsize)            {                maxsize = tree[p->v].size;                tree[x].ch = tree + p->v;            }        }    }}//连接重链void dfs2(int x,int ancestor){    tree[x].vis = true;    tree[x].tid = ++static_id;    tree[x].top = ancestor;    if(tree[x].ch!=null)    {        dfs2(tree[x].ch->id,ancestor);    }    for(ANode * p = adj[x]; p; p = p->next)    {        if(tree[p->v].vis == false)        {            dfs2(tree[p->v].id,tree[p->v].id);        }    }}void build(int l,int r,int x){    if(l == r)    {        M[x] = A[l];        return;    }    build(l,MID,lx);    build(MID+1,r,rx);    M[x] = max(M[lx],M[rx]);}//在线段树中查找,返回某一个区间内的最大值int query2(int l,int r,int x,int L,int R){    if(L<=l && R>=r)    {        return M[x];    }    int temp = 0;    if(L<= MID)    {        temp = max(temp,query2(l,MID,lx,L,R));    }    //注意千万不要加else ,否则会WA    if(R > MID)    {        temp = max(temp,query2(MID+1,r,rx,L,R));    }    return temp;}//在原有树中查找,返回两个节点中的最大值int query(int a,int b){    int ans = 0;    while(tree[a].top != tree[b].top)    {        if(tree[tree[a].top].deep < tree[tree[b].top].deep)        {            swap(a,b);        }        ans = max(ans,query2(2,N,1,tree[tree[a].top].tid,tree[a].tid));        a = tree[tree[a].top].father->id;    }    if(tree[a].deep > tree[b].deep)    {        swap(a,b);    }    if(a!=b)    {        ans = max(ans,query2(2,N,1,tree[a].tid + 1,tree[b].tid));    }    return ans;}void update(int l,int r,int x,int p,int c){    if(l == r)    {        M[x] = c;        return;    }    if(p<=MID)    {        update(l,MID,lx,p,c);    }    else    {        update(MID+1,r,rx,p,c);    }    M[x] = max(M[lx],M[rx]);}void change(int a,int b){    if(tree[edge[a].first].deep > tree[edge[a].second].deep)    {        update(2,N,1,tree[edge[a].first].tid,b);    }    else    {        update(2,N,1,tree[edge[a].second].tid,b);    }}int main(){#ifndef ONLINE_JUDGE    freopen("in.txt","r",stdin);#endif    int a,b,c;    char cmd[20];    scanf("%d",&T);    null = &Tnull;    init(null);    null->id = -1;    while(T--)    {        scanf("%d",&N);        static_id = 0;        memset(A,0,sizeof(A));        memset(M,0,sizeof(M));        for(int i=1; i<=N; i++)        {            adj[i] = NULL;            init(&tree[i]);            tree[i].id = i;        }        edge.clear();        for(int i=0; i<N-1; i++)        {            scanf("%d%d%d",&a,&b,&c);            adj[a] = new ANode(b,c,adj[a]);            adj[b] = new ANode(a,c,adj[b]);            edge.push_back(make_pair(a,b));        }        dfs(1,0,1);        for(int i=1; i<=N; i++)        {            tree[i].vis = false;        }        dfs2(1,1);        for(int i = 0; i<N-1; i++)        {            if(tree[edge[i].first].deep > tree[edge[i].second].deep)            {                A[tree[edge[i].first].tid] = tree[edge[i].first].cost;            }            else            {                A[tree[edge[i].second].tid] = tree[edge[i].second].cost;            }        }        build(2,N,1);//对数组A build 线段树        while(1)        {            scanf("%s",cmd);            if(strcmp(cmd,"QUERY") == 0)            {                scanf("%d%d",&a,&b);                printf("%d\n",query(a,b));            }            if(strcmp(cmd,"CHANGE") == 0)            {                scanf("%d%d",&a,&b);                a--;                change(a,b);            }            if(strcmp(cmd,"DONE")==0)            {                break;            }        }    }    return 0;}


其中,由于线段树我好久没写,遗忘了一些,特别是有个及其容易忽略的地方,贴下来,警示后者:

对于query2这个函数,及,查询某一区间的最值,切忌不要加else,也就是说要双向比较:

理由如图:

比如对于Qtree我有这样一组数据:

1122 1 83 1 24 1 73 5 38 3 65 6 55 7 49 6 110 9 27 11 27 12 10QUERY 5 12DONE

那么,QUERY 5 12 应该是第 4 、5条边的极大值,

如果我这样写:

if(L<= MID)    {        temp = max(temp,query2(l,MID,lx,L,R));    }    //注意千万不要加else ,否则会WAelse  if(R > MID)    {        temp = max(temp,query2(MID+1,r,rx,L,R));    }
就会出错。理由:




4,5跨两个区间。


原创粉丝点击