HYSBZ 1036 【树链剖分】

思路:

裸裸的树链剖分....

树链剖分就是把一棵树分成若干重链和轻链...然后保证形成的线段树上每条链是连续存储的。然后这样就能用线段树进行维护了。

但是每次一定要保证是在同一条链里边....思路就是这样....

感觉最近越来越淡定了,题目卡住了也不怎么着急了,慢慢看代码...嗯...有点像学习的感觉了....

明天青岛理工的邀请赛要好好玩玩...

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n,m;
int myw[30100];
bool vis[30100];
int siz[30100],dep[30100],top[30100],fa[30100],son[30100],w[30100],fp[30100];
int totw;
char typ[20];
long long INF=0x3f3f3f3f3f;
struct edge
{
    int id;
    edge *next;
};
edge edges[30100<<1];
edge *adj[30100];
struct tr
{
    int s,e,maxx;
    long long sum;
};
tr tree[30100<<2];
void build(int k,int s,int e)
{
    tree[k].s=s;
    tree[k].e=e;
    if(s==e)
    {
        tree[k].sum=tree[k].maxx=myw[fp[s]];
        return;
    }
    int mid=(s+e)>>1;
    build(k<<1,s,mid);
    build(k<<1|1,mid+1,e);
    tree[k].maxx=max(tree[k<<1].maxx,tree[k<<1|1].maxx);
    tree[k].sum=tree[k<<1|1].sum+tree[k<<1].sum;
}
int ednum;
inline void AddEdge(int a,int b)
{
    edge *tmp;
    tmp=&edges[ednum++];
    tmp->id=b;
    tmp->next=adj[a];
    adj[a]=tmp;
}

void dfs1(int id,int mdep,int f)
{
    vis[id]=1;
    dep[id]=mdep;
    siz[id]=1;
    fa[id]=f;
    son[id]=0;
    int tmpsiz=-1;
    for(edge *it=adj[id]; it; it=it->next)
    {
        if(!vis[it->id])
        {
            dfs1(it->id,mdep+1,id);
            if(tmpsiz<siz[it->id])
            {
                tmpsiz=siz[it->id];
                son[id]=it->id;
            }
            siz[id]+=siz[it->id];
        }
    }
}
void dfs2(int id,int ase)
{
    vis[id]=1;
    top[id]=ase;
    w[id]=++totw;
    fp[totw]=id;
    if(son[id])
        dfs2(son[id],ase);
    for(edge *it=adj[id]; it; it=it->next)
    {
        if(!vis[it->id])
        {
            dfs2(it->id,it->id);
        }
    }
}
long long ans;
void querySum(int k,int s,int e)
{
    if(tree[k].e==e&&tree[k].s==s)
    {
        ans+=tree[k].sum;
        return;
    }
    int mid=(tree[k].s+tree[k].e)>>1;
    if(e<=mid)
    {
        querySum(k<<1,s,e);
    }
    else if(s>mid)
    {
        querySum(k<<1|1,s,e);
    }
    else
    {
        querySum(k<<1,s,mid);
        querySum(k<<1|1,mid+1,e);
    }
}
void qsum(int a,int b)
{
    int f1=top[a],f2=top[b];

    ans=0;
    while(f1!=f2)
    {
        if(dep[f1]<dep[f2])
        {
            swap(f1,f2);
            swap(a,b);
        }
        querySum(1,w[f1],w[a]);
        a=fa[f1];
        f1=top[a];
    }
    if(dep[a]>dep[b])
        swap(a,b);
    querySum(1,w[a],w[b]);
    printf("%lld\n",ans);
}
void queryMax(int k,int s,int e)
{
    if(tree[k].e==e&&tree[k].s==s)
    {
        ans=max(ans,(long long)tree[k].maxx);
        return;
    }
    int mid=(tree[k].s+tree[k].e)>>1;
    if(e<=mid)
    {
        queryMax(k<<1,s,e);
    }
    else if(s>mid)
    {
        queryMax(k<<1|1,s,e);
    }
    else
    {
        queryMax(k<<1,s,mid);
        queryMax(k<<1|1,mid+1,e);
    }
}
void qmax(int a,int b)
{
    int f1=top[a],f2=top[b];
    ans=INF*(-1);
    while(f1!=f2)
    {
        if(dep[f1]<dep[f2])
        {
            swap(f1,f2);
            swap(a,b);
        }
        queryMax(1,w[f1],w[a]);
        a=fa[f1];
        f1=top[a];
    }
    if(dep[a]>dep[b])
        swap(a,b);
    queryMax(1,w[a],w[b]);
    printf("%lld\n",ans);
}
void update(int k,int pos,int val)
{
    if(tree[k].s==tree[k].e)
    {
        tree[k].sum=tree[k].maxx=val;
        return;
    }
    int mid=(tree[k].s+tree[k].e)>>1;
    if(pos<=mid)
        update(k<<1,pos,val);
    else
        update(k<<1|1,pos,val);
    tree[k].sum=tree[k<<1|1].sum+tree[k<<1].sum;
    tree[k].maxx=max(tree[k<<1].maxx,tree[k<<1|1].maxx);
}
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        memset(siz,0,sizeof(siz));
        memset(vis,0,sizeof(vis));
        memset(son,0,sizeof(son));
        fa[1]=0;
        totw=0;
        ednum=0;
        for(int i=1;i<=n;i++)
        {
            adj[i]=NULL;
        }
        for(int i=1; i<n; i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            AddEdge(a,b);
            AddEdge(b,a);
        }
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&myw[i]);
        }
        dfs1(1,1,0);
        memset(vis,0,sizeof(vis));
        dfs2(1,1);
        build(1,1,totw);
        scanf("%d",&m);
        for(int i=1; i<=m; i++)
        {
            int a,b;
            scanf("%s%d%d",typ,&a,&b);
            if(typ[3]==‘X‘)
            {
                qmax(a,b);
            }
            else if(typ[3]==‘M‘)
            {
                qsum(a,b);
            }
            else if((typ[3]==‘N‘))
            {
                update(1,w[a],b);
            }
        }
    }
}
/*
8
1 2
1 4
2 3
3 8
5 6
7 5
2 5
3 7 6 5 8 2 1 0
1000
*/
时间: 2024-10-08 10:45:47

HYSBZ 1036 【树链剖分】的相关文章

HYSBZ 1036 树链剖分(单点更新区间求和求最大值)

http://www.lydsy.com/JudgeOnline/problem.php?id=1036 Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 Input 输入

HYSBZ 1036 树链剖分

点击打开链接 题意:中文 思路:和最简单的HDU 3966 一样,我们只需要用线段树维护两个值就行了,一个最大值一个和,然后注意一下的就是有的题目是点权有的题目是边权 #pragma comment(linker, "/STACK:1024000000,1024000000") #include <vector> #include <stdio.h> #include <string.h> #include <stdlib.h> #inc

HYSBZ 2243 树链剖分(区间更新,区间查询)较难

http://www.lydsy.com/JudgeOnline/problem.php?id=2243 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如"112221"由3段组成:"11"."222"和"1". 请你写一个程序依次完成这m个操作. Input 第一行包含2个整数

bzoj 1036 树链剖分+线段树 裸题

HYSBZ - 1036 题意:中文题 思路:树链剖分裸题,线段树写得比较搓,(在线段树上修改节点u的时候应该修改u映射到线段树后的节点序号,这里wa了半年,真的是半年) AC代码: #include "iostream" #include "string.h" #include "stack" #include "queue" #include "string" #include "vector

spoj 375 AND bzoj 1036 树链剖分

树链剖分的入门题目,自己写了下感觉还是挺好写的,不过真的有点长... spoj 375 边有权值: 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 6 const int INF = -999999999; 7 const int N = 10001; 8 int head[N]; 9 int sz[N]; 10 int depth[N]; 1

HYSBZ 2243 树链剖分

点击打开链接 题意:中文 思路:一看就是应该用树链剖分与线段树的结束,主要是结合什么呢,因为需要连续的一段一段的,所以我们肯定是要用区间合并,那么区间合并需要用到什么呢,就是整个区间的最左端和最右端的元素及这个区间已经形成的段数,那么我们合并的时候就要判断一下左儿子的最右端与右儿子的最左端是不是相同,然后在处理,而更新就用个懒惰标记就可以完成了,而查询一段是与正常的区间合并是一样的,但是因为是树链剖分,将路径结合到线段树的部分是有一部分的区间是有联合的,所以我们不能直接将和加起来,还是要判断一下

HYSBZ - 2243 树链剖分 + 线段树 处理树上颜色段数

用线段树处理颜色段数 记录区间内的颜色段数,区间右端点的颜色,区间右端点的颜色. int tr[maxn<<2], lc[maxn<<2], rc[maxn<<2]; 懒标记,记录区间是否被覆盖 int lazy[maxn<<2]; 合并的方法是这样,对于某一区间 ? 如果 左区间的右端点颜色 == 右区间的左端点 ? 那么 这左右区间合并,左区间的最右边一段和右区间最左边一段颜色是连续的,那么区间的颜色段数为 左区间颜色段数+右区间颜色段数 - 1. ?

BZOJ 1036 树链剖分模板题

BZOJ 1036 题意:一棵树,每个点有权值,三种操作:修改一个点的值:询问一条链上最大值:询问一条链上权值和. tags:模板题 // bzoj 1036 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define FF(i,a,b) for (int i=a;i<=b;i++) #define F(i,b,a)

BZOJ 1036 [ZJOI2008]树的统计Count (树链剖分 - 点权剖分 - 单点权修改)

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1036 树链剖分模版题,打的时候注意点就行.做这题的时候,真的傻了,单词拼错检查了一个多小时... 代码如下: 1 //树链剖分 点权修改 修改单节点 2 #include <iostream> 3 #include <cstring> 4 #include <algorithm> 5 #include <cstdio> 6 using namespa

HYSBZ 1036 树的统计Count(树链剖分)

HYSBZ 1036 树的统计Count 题目链接 就树链剖分,线段树维护sum和maxx即可 代码: #include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; const int N = 30005; int dep[N], fa[N], son[N], sz[N], top[N], id[N], idx, val[N];