BZOJ3730震波——动态点分治+线段树(点分树套线段树)

题目描述

在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i]。
不幸的是,这片土地常常发生地震,并且随着时代的发展,城市的价值也往往会发生变动。
接下来你需要在线处理M次操作:
0 x k 表示发生了一次地震,震中城市为x,影响范围为k,所有与x距离不超过k的城市都将受到影响,该次地震造成的经济损失为所有受影响城市的价值和。
1 x y 表示第x个城市的价值变成了y。
为了体现程序的在线性,操作中的x、y、k都需要异或你程序上一次的输出来解密,如果之前没有输出,则默认上一次的输出为0。

输入

第一行包含两个正整数N和M。
第二行包含N个正整数,第i个数表示value[i]。
接下来N-1行,每行包含两个正整数u、v,表示u和v之间有一条无向边。
接下来M行,每行包含三个数,表示M次操作。

输出

包含若干行,对于每个询问输出一行一个正整数表示答案。

样例输入

8 1
1 10 100 1000 10000 100000 1000000 10000000
1 2
1 3
2 4
2 5
3 6
3 7
3 8
0 3 1

样例输出

11100101

提示

1<=N,M<=100000
1<=u,v,x<=N
1<=value[i],y<=10000
0<=k<=N-1

 

 

我们考虑单次询问用点分治如何处理?假设查询点为x。

1、如果当前分治联通块中没有x,那么不用再分治统计了;

2、如果当前联通块中有x并且x不是分治中心,假设当前分治中心与x间的距离为d,我们需要找到与分治中心距离<=k-d的所有点的点权和。但这样统计有不合法的点,即与x在分治中心的同一棵子树中的点,所有我们还需要统计x所在的分治中心的那棵子树中与分治中心距离<=k-d的所有点权和,将这部分答案减掉即可。

3、如果x为当前分治中心,那么直接统计当前联通块中与x距离<=k的点的点权和即可。

其实第三种情况可以归为第二种情况中,那么我们就可以得出每个点需要维护的信息(其中k为任意距离,为了方便我们称以一个点为分治中心时能遍历到的点组成的联通块为这个点管辖的联通块):

1、以当前点为分治中心时联通块中与这个点距离<=k的点权和,这个用线段树维护每个距离的点权和即可。

2、以当前点在原树上的父节点为分治中心时,这个点在原树的子树中与这个点父节点距离<=k的点权和,这个同样用线段树维护,但是我们知道当前点在点分树上无法维护这个联通块的信息,所以这个联通块的信息由这个联通块的重心的那棵线段树来维护。也就变成了每个点维护点分树上这个点子树中所有点与这个点在点分树上的父节点距离<=k的点的点权和。

那么修改就很好办了,因为一个点的点权只被这个点在点分树上到根路径上所有点保存,只要暴力往根爬然后修改沿途节点线段树中信息即可。

对于查询,因为我们只需要知道管辖的联通块中包含查询点的点的信息即可,而这些点就是查询点在点分树上到根路径上的所有点,我们依旧暴力往根爬并统计沿途点的答案即可。

时间复杂度O(nlogn^2)

这个题很卡常,对于每个点要求出以它为分治中心时联通块中的最大深度作为这个点维护的线段树的上界,这样能减少递归层数。并且线段树数组要用结构体存,还要加读入优化……

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mid ((l+r)>>1)
using namespace std;
inline char _read()
{
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    int x=0,f=1;char ch=_read();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=_read();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=_read();}
    return x*f;
}
int n,m;
int tot;
int num;
int cnt;
int dfn;
int opt;
int x,y;
int ans;
int rot;
int res;
int mxp;
int f[100010];
int s[100010];
int d[100010];
int mn[100010];
int lg[200010];
int to[200010];
int mx[100010];
int dep[100010];
int val[100010];
int vis[100010];
int next[200010];
int head[100010];
int size[100010];
int root[100010];
int froot[100010];
int g[18][200010];
struct miku
{
    int ls,rs,sum;
}tr[9000000];
inline void add(int x,int y)
{
    next[++tot]=head[x],head[x]=tot,to[tot]=y;
}
void dfs(int x,int fa)
{
    g[0][++dfn]=dep[x];
    s[x]=dfn;
    for(int i=head[x];i;i=next[i])
    {
        if(to[i]!=fa)
        {
            dep[to[i]]=dep[x]+1;
            dfs(to[i],x);
            g[0][++dfn]=dep[x];
        }
    }
}
inline void getroot(int x,int fa)
{
    mx[x]=0;
    size[x]=1;
    for(int i=head[x];i;i=next[i])
    {
        if(!vis[to[i]]&&to[i]!=fa)
        {
            getroot(to[i],x);
            size[x]+=size[to[i]];
            mx[x]=max(mx[x],size[to[i]]);
        }
    }
    mx[x]=max(mx[x],num-size[x]);
    if(mx[x]<mx[rot])
    {
        rot=x;
    }
}
void find_dep(int x,int fa)
{
    d[x]=d[fa]+1;
    mxp=max(mxp,d[x]);
    for(int i=head[x];i;i=next[i])
    {
        if(!vis[to[i]]&&to[i]!=fa)
        {
            find_dep(to[i],x);
        }
    }
}
inline void partation(int x)
{
    vis[x]=1;
    mxp=0;
    find_dep(x,0);
    mn[x]=mxp;
    for(int i=head[x];i;i=next[i])
    {
        if(!vis[to[i]])
        {
            num=size[to[i]];
            rot=0;
            getroot(to[i],0);
            f[rot]=x;
            partation(rot);
        }
    }
}
inline int lca(int x,int y)
{
    x=s[x];
    y=s[y];
    if(x>y)
    {
        swap(x,y);
    }
    int len=lg[y-x+1];
    return min(g[len][x],g[len][y-(1<<len)+1]);
}
inline int dis(int x,int y)
{
    return dep[x]+dep[y]-(lca(x,y)<<1);
}
inline void insert(int &rt,int l,int r,int k,int x)
{
    if(!rt)
    {
        rt=++cnt;
    }
    tr[rt].sum+=x;
    if(l==r)
    {
        return ;
    }
    if(k<=mid)
    {
        insert(tr[rt].ls,l,mid,k,x);
    }
    else
    {
        insert(tr[rt].rs,mid+1,r,k,x);
    }
}
inline int query(int rt,int l,int r,int k)
{
    if(!rt)
    {
        return 0;
    }
    if(l==r)
    {
        return tr[rt].sum;
    }
    if(k<=mid)
    {
        return query(tr[rt].ls,l,mid,k);
    }
    else
    {
        return tr[tr[rt].ls].sum+query(tr[rt].rs,mid+1,r,k);
    }
}
inline void change(int x,int val)
{
    for(int i=x;i;i=f[i])
    {
        insert(root[i],0,mn[i],dis(x,i),val);
        if(f[i])
        {
            insert(froot[i],0,mn[f[i]],dis(x,f[i]),val);
        }
    }
}
inline int query(int x,int k)
{
    res=0;
    for(int i=x;i;i=f[i])
    {
        if(dis(x,i)<=k)
        {
            res+=query(root[i],0,mn[i],k-dis(x,i));
        }
        if(f[i]&&dis(x,f[i])<=k)
        {
            res-=query(froot[i],0,mn[f[i]],k-dis(x,f[i]));
        }
    }
    return res;
}
int main()
{
    n=read();
    m=read();
    for(int i=1;i<=n;i++)
    {
        val[i]=read();
    }
    for(int i=1;i<n;i++)
    {
        x=read();
        y=read();
        add(x,y);
        add(y,x);
    }
    dfs(1,0);
    for(int i=2;i<=dfn;i++)
    {
        lg[i]=lg[i>>1]+1;
    }
    for(int j=1;(1<<j)<=dfn;j++)
    {
        for(int i=1;i+(1<<j)-1<=dfn;i++)
        {
            g[j][i]=min(g[j-1][i],g[j-1][i+(1<<(j-1))]);
        }
    }
    mx[0]=1<<30;
    num=n;
    getroot(1,0);
    partation(rot);
    for(int now=1;now<=n;now++)
    {
        for(int i=now;i;i=f[i])
        {
            insert(root[i],0,mn[i],dis(now,i),val[now]);
            if(f[i])
            {
                insert(froot[i],0,mn[f[i]],dis(now,f[i]),val[now]);
            }
        }
    }
    while(m--)
    {
        opt=read();
        x=read()^ans;
        y=read()^ans;
        if(!opt)
        {
            printf("%d\n",ans=query(x,y));
        }
        else
        {
            change(x,y-val[x]);
            val[x]=y;
        }
    }
}
posted @ 2018-12-11 10:33  The_Virtuoso  阅读(483)  评论(0编辑  收藏  举报