hdu-3966(树链剖分+线段树)

做之前了解树链剖分的相关内容。

题意:给出一棵树,每个节点有一些敌人,有三种操作,I:x,y,路径上的所有点的人数+w。D:x,y,路径上的所有点的人数-w。Q:节点x的人数。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include <iostream>
using namespace std;
const int N=100005;
int son[N];//重儿子;
int sz[N];//以该节点为根的子结点数(包括自己)
int dep[N];//该节点到根的距离
int top[N];//该节点所在重链最顶端节点
int father[N];//每个节点的父节点
int head[N];//用于构造邻接表
int ti[N];//该点映射到线段树中的位置
int num,idx,a[N];
struct edge
{
    int st;
    int next;
}e[N];
void addedge(int x,int y)//构造邻接表存储边
{
    e[num].st=x;e[num].next=head[y];head[y]=num++;
    e[num].st=y;e[num].next=head[x];head[x]=num++;
}
//****************树链剖分****************
void find_son(int u)//第一次dfs求出father·dep·size·son数组
{
    int i,v;
    sz[u]=1;son[u]=0;
    for(i=head[u];i!=-1;i=e[i].next)
    {
        v=e[i].st;
        if(v==father[u]) continue;
        father[v]=u;dep[v]=dep[u]+1;
        find_son(v);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v;
    }
}
void find_ti(int u,int fa)//第二次dfs求top和ti数组
{
    int i,v;
    ti[u]=idx++;
    top[u]=fa;
    if(son[u]!=0) find_ti(son[u],top[u]);
    for(i=head[u];i!=-1;i=e[i].next)
    {
        v=e[i].st;
        if(v==son[u]||v==father[u]) continue;
        find_ti(v,v);
    }
}
//***************线段树****************
struct Tree
{
    int r,l;
    int ct;//该区间的增量
}T[N];
void buildtree(int l,int r,int id)//建线段树
{
    T[id].l=l;
    T[id].r=r;
    T[id].ct=0;
    if(l==r) return ;
    int mid=(l+r)>>1;
    int li=id<<1;
    int ri=li+1;
    buildtree(l,mid,li);
    buildtree(mid+1,r,ri);
}
void Insert(int l,int r,int id,int w)//为线段树保存的l~r区间增加w
{
    if(T[id].l==l&&T[id].r==r)
    {
        T[id].ct+=w;
        return;
    }
    int mid=(T[id].l+T[id].r)>>1;
    int li=id<<1;
    int ri=li+1;
    if(mid>=r) Insert(l,r,li,w);
    else if(mid<l) Insert(l,r,ri,w);
    else {
        Insert(l,mid,li,w);
        Insert(mid+1,r,ri,w);
    }
}
int findw(int i,int id,int w)  //线段树区间的查询操作
{
    if(T[id].l==T[id].r)
        return w+T[id].ct;
    int mid=(T[id].l+T[id].r)>>1,li=id<<1,ri=li+1;
    if(mid>=i)return findw(i,li,w+T[id].ct);
    else return findw(i,ri,w+T[id].ct);
}
void lca(int x,int y,int w)//为原题要求x~y路径上各点增加w
{
    while(top[x]!=top[y])//不在同一条重边上,不断更新当前所在重边
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        Insert(ti[top[x]],ti[x],1,w);
        x=father[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);//最终在同一条重边
    Insert(ti[x],ti[y],1,w);
}
int main()
{
    int i,n,m,q,x,y,w;
    char str[10];
    while(scanf("%d%d%d",&n,&m,&q)!=EOF)
    {
        memset(head,-1,sizeof(head));
        num=0;
        for(i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            addedge(x,y);
        }
        father[1]=sz[0]=0;dep[1]=idx=1;
        find_son(1);
        find_ti(1,1);
        buildtree(1,n,1);
        while(q--)
        {
            scanf("%s",str);
            if(str[0]=='Q')
            {
                scanf("%d",&x);
                printf("%d\n",a[x]+findw(ti[x],1,0));
            }
            else
            {
                scanf("%d%d%d",&x,&y,&w);
                if(str[0]=='I') w=-w;
                lca(x,y,-w);
            }
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值