【BZOJ4372】烁烁的游戏 动态树分治

讲道理我第一次看到动态树分治以为和那个动态树LCT有关QAQ

然而正确的读法是【动态(树分治)】

这就很尴尬了。

题目大意就是给定一棵树,每个点初始权值是0,维护两个操作,一是将和某一个点距离不超过d的点的权值都加上v,二是查询某两个点之间的权值。

首先用点分治建出新的树结构,利用标记永久化的思想为每个点建立一棵线段树记录距离这个点距离为d的点的权值之和,统计的时候再求一下和即可。

唯一要注意的是,为了防止两个节点算重要给每个儿子节点多开开一棵线段树。

/**************************************************************
    Problem: 4372
    User: RicardoWang
    Language: C++
    Result: Accepted
    Time:22804 ms
    Memory:264684 kb
****************************************************************/
 
#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
void _read(int &x)
{
    x=0; char ch=getchar(); bool flag=false;
    while(ch<'0' || ch>'9'){if(ch=='-')flag=true; ch=getchar();}
    while(ch>='0' && ch<='9'){x=x*10+ch-'0'; ch=getchar();} if(flag)x=-x; return ;
}
#define MAXN 200005
struct edge
{
    int to,next;
}e[MAXN*2];
int n,m,edge_ct,head[MAXN];
bool del[MAXN];
void add(int x,int y)
{
    e[++edge_ct]=(edge){y,head[x]}; head[x]=edge_ct;
    e[++edge_ct]=(edge){x,head[y]}; head[y]=edge_ct; return ;
}
int root,sz[MAXN],c[MAXN],f[MAXN],d[MAXN],fb[MAXN][20];
void getroot(int x,int fa,int sum)
{
    sz[x]=1; c[x]=0;
    for(int id=head[x];id;id=e[id].next)
    {
        if(e[id].to==fa || del[e[id].to])continue;
        getroot(e[id].to,x,sum); sz[x]+=sz[e[id].to]; c[x]=max(c[x],sz[e[id].to]);
    }
    c[x]=max(sum-sz[x],c[x]);
    if(c[x]<c[root])root=x;
    return ;
}
void _DFS(int now,int fa)
{
    sz[now]=1;
    for(int id=head[now];id;id=e[id].next)
    {
        if(del[e[id].to] || e[id].to==fa)continue;
        _DFS(e[id].to,now);
        sz[now]+=sz[e[id].to];
    }
    return ;
}
void _build(int now,int fa,int sum)
{
    root=0; getroot(now,0,sum);
    int nowroot=root;
    del[nowroot]=true; f[nowroot]=fa;
    _DFS(nowroot,0);
    for(int id=head[nowroot];id;id=e[id].next)
    {
        if(del[e[id].to])continue;
        _build(e[id].to,nowroot,sz[e[id].to]);
    }
    return ;
}
void DFS_0(int now,int fa)
{
    for(int id=head[now];id;id=e[id].next)
    {
        if(e[id].to==fa)continue;
        d[e[id].to]=d[now]+1; f[e[id].to]=now;
        DFS_0(e[id].to,now);
    }
    return ;
}
void makesa()
{
    for(int i=1;i<=n;i++)fb[i][0]=f[i];
    for(int k=1;k<=17;k++)
    {
        for(int i=1;i<=n;i++)fb[i][k]=fb[fb[i][k-1]][k-1];
    }
    return ;
}
int LCA(int x,int y)
{
    if(d[x]<d[y])swap(x,y);
    for(int k=17;k>=0;k--)if(d[x]-d[y]>=(1<<k))
    {
        x=fb[x][k];
    }
    if(x==y)return x;
    for(int k=17;k>=0;k--)if(fb[x][k]!=fb[y][k])
    {
        x=fb[x][k]; y=fb[y][k];
    }
    return fb[x][0];
}
int getdis(int x,int y)
{
    return d[x]+d[y]-2*d[LCA(x,y)];
}
void Init()
{
    _read(n); _read(m);
    int x,y;
    for(int i=1;i<n;i++){_read(x); _read(y); add(x,y);}
    DFS_0(1,0);makesa();
    memset(f,0,sizeof(f));
    c[0]=n+1;_build(1,0,n);
    return ;
}
int np,rt1[MAXN],rt2[MAXN],chi[100*MAXN][2],w[100*MAXN];
void update(int &now,int L,int R,int x,int y,int de)
{
    if(!now){now=++np; w[now]=0;}
    if(x<=L && R<=y){w[now]+=de; return ;}
    int m=(L+R)>>1;
    if(x<=m)update(chi[now][0],L,m,x,y,de);
    if(y>m)update(chi[now][1],m+1,R,x,y,de);
    return ;
}
int query(int &now,int L,int R,int x)
{
    int ans=w[now];
    if(!now || L==R)return w[now];
    int m=(L+R)>>1;
    return x<=m ? ans+query(chi[now][0],L,m,x): ans+query(chi[now][1],m+1,R,x);
}
void MM(int x,int y,int z)
{
    int dis;
    update(rt1[x],0,n,0,y,z);
    for(int i=x;f[i];i=f[i])
    {
        dis=getdis(x,f[i]);if(dis>y)continue;
        update(rt1[f[i]],0,n,0,y-dis,z);
        update(rt2[i],0,n,0,y-dis,z);
    }
    return ;
}
int QQ(int now)
{
    int ans=0,dis;
    ans+=query(rt1[now],0,n,0);
    for(int i=now;f[i];i=f[i])
    {
        dis=getdis(f[i],now);
        ans+=query(rt1[f[i]],0,n,dis)-query(rt2[i],0,n,dis);
    }
    return ans;
}
void work()
{
    char op;
    int x,y,z;
    for(int i=1;i<=m;i++)
    {
        op=getchar(); while(op!='M' && op!='Q')op=getchar();
        if(op=='Q')
        {
            _read(x); 
            printf("%d\n",QQ(x));
        }
        else
        {
            _read(x); _read(y); _read(z); MM(x,y,z);
        }
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    Init();
    work();
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值