SPOJ 375 QTREE (树链剖分入门题)

题目大意:对一棵有n(n<=10000)个点的树若干次操作,每次操作可以把第i条边的权值改为w,或者求u节点到v节点的路径上所有边的最大权值。

解题思路:对这棵树进行树链剖分,然后用线段树维护区间最大值。

树链剖分理解:通过规定一种特殊的次序,将树形结构变为线性结构,从而可以用多种数据结构来解决树上路径方面的问题。

AC代码:

/*
    @Author: wchhlbt
    @Date:   2017/7/22
*/
#include <bits/stdc++.h>

#define Fori(x) for(int i=0;i<x;i++)
#define Forj(x) for(int j=0;j<x;j++)
#define maxn 10005
#define inf 0x3f3f3f3f
#define ONES(x) __builtin_popcount(x)
#define pb push_back
#define _  << "  " <<
using namespace std;

typedef long long ll ;
const double eps =1e-8;
const int mod = 1000000007;
typedef pair<int, int> P;
const double PI = acos(-1.0);
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};

int n,m;
int ans;
int cnt;

struct edge{
    int u,v,val;
    void read(){
        scanf("%d%d%d",&u,&v,&val);
    }
}edge[maxn];
vector<int> e[maxn];


/*树链剖分
    cnt初始化为0
    son数组初始化为-1
    getans()根据实际维护的值进行修改
*/

int son[maxn];//重儿子
int size[maxn];//子数大小
int fa[maxn];//父节点
int dep[maxn];//深度
void dfs1(int u, int f, int d)//维护重儿子,深度,父节点,子树大小
{
    size[u] = 1;//初始化size
    fa[u] = f;//标注父节点
    dep[u] = d;
    son[u] = 0;//初始化重儿子编号为0
    for(int i = 0; i<e[u].size(); i++){
        int v = e[u][i];
        if(v==f)    continue;
        dfs1(v,u,d+1);
        size[u] += size[v];
        if(size[v]>size[son[u]])
            son[u] = v;
    }
}

int id[maxn];
int top[maxn];
//dfs2建立重链
void dfs2(int u, int tp)//tp为当前重链的起始点
{
    top[u] = tp;
    id[u] = ++cnt;
    if(son[u]!=-1)  dfs2(son[u],tp);
    for(int i = 0; i<e[u].size(); i++){
        int v = e[u][i];
        if(v==fa[u] || v==son[u])    continue;
        dfs2(v,v);
    }
}
//线段树
int val[maxn];
//区间最大值线段树  支持单点修改
struct NODE
{
    int l,r,val;
} node[4*maxn];

int build(int root, int l, int r)
{
    node[root].l = l;
    node[root].r = r;
    if(l==r)
        return node[root].val = val[l];
    int mid = (l+r)>>1;
    int a = build(root<<1, l, mid);
    int b = build(root<<1 | 1 ,mid+1, r);
    return node[root].val = max(a,b);
}

int update(int root, int pos, int val)//将pos位置的值替换为val
{
    if (pos < node[root].l || pos > node[root].r)
        return node[root].val;
    if (node[root].l == pos && node[root].r == pos)
        return node[root].val = val;
    int a = update (root<<1, pos, val);
    int b = update (root<<1 | 1 , pos, val);
    node[root].val = max(a,b);
    return node[root].val;

}

int query(int root, int l, int r)
{
    if(l>node[root].r || r<node[root].l)//无交集
        return 0;
    if(l<=node[root].l && node[root].r<=r)//此区间包含root所管理的区间
        return node[root].val;
    int a = query(root<<1, l, r);//部分相交
    int b = query(root<<1 | 1 ,l, r);
    return max(a,b);
}
/*****************************************/
int getans(int u, int v)
{
    int tp1 = top[u]; int tp2 = top[v];
    int ans = 0;
    while(tp1!=tp2){
        if(dep[tp2]>dep[tp1]){
            swap(u,v);
            swap(tp1,tp2);
        }
        int temp = query(1,id[tp1],id[u]);//考虑tp1上面的轻链
        ans = max(ans,temp);
        u = fa[tp1];    tp1 = top[u];//更新u节点为重链头节点的父节点
    }
    if(u==v)    return ans;
    //处理u、v在一条重链上的情况
    if(dep[v]>dep[u])   swap(u,v),swap(tp1,tp2);
    int temp = query(1,id[son[v]],id[u]);
    ans = max(ans,temp);
    return ans;
}

void init()//全部初始化操作
{
    cnt = 0;//重新编号树上的id
    memset(son,-1,sizeof(son));
    for(int i = 0; i<maxn-2; i++)
        e[i].clear();
}

int main()
{
    //freopen("test.txt","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--){
        init();
        scanf("%d",&n);
        for(int i = 1; i<n; i++){
            edge[i].read();
            e[edge[i].u].pb(edge[i].v);
            e[edge[i].v].pb(edge[i].u);
        }
        dfs1(1,0,1);
        dfs2(1,1);
        for (int i = 1; i < n; i++) {//将u节点调整成更深的节点
            if (dep[edge[i].u] < dep[edge[i].v]) swap(edge[i].u, edge[i].v);
            val[id[edge[i].u]] = edge[i].val;
        }
        build(1,1,cnt);
        char s[200];
        while(~scanf("%s",&s) && s[0]!='D'){
            int x,y;
            scanf("%d%d",&x,&y);
            if(s[0]=='Q')
                printf("%d\n",getans(x,y));
            else
                update(1,id[edge[x].u],y);//单点更新
        }
    }

    return 0;
}

/*

unsigned   int   0~4294967295
int   2147483648~2147483647
unsigned long 0~4294967295
long   2147483648~2147483647
long long的最大值:9223372036854775807
long long的最小值:-9223372036854775808
unsigned long long的最大值:18446744073709551615

__int64的最大值:9223372036854775807
__int64的最小值:-9223372036854775808
unsigned __int64的最大值:18446744073709551615

*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值