hdu4918 Query on the subtree

  树分治,设当前树的分治中心为x,其子树分治中心为y,则设father[y]=x,分治下去则可以得到一颗重心树,而且树的深度是logn。

  询问操作(x,d),只需要查询重心树上x到重心树根节点上的节点的累加和。假设当前节点是y,那么节点y可以贡献的答案是那些以y为分治中心且到y距离为d-dis(x,y)的节点的总和。当然这样可能会出现重复的情况,重复情况只会出现在包含x的那颗子树上,因此减掉即可。修改操作类似。复杂度O(nlognlogn)

代码

#include<cstdio>
#include<cstring>
#define N 200010
#define LL long long
using namespace std;
int dp,pre[N],p[N],tt[N],vis[N],father[N],s[N],tmp,m;
int n,a,b,i,w[N],L,cnt,tot,len[N],Len[N],start[N],Start[N],v[N];
int deep[N],ss[N][21],fa[N];
int c[N*50];
int min(int a,int b)
{
    if (a<b) return a;return b;
}
int lowbit(int x)
{
    return x&(-x);
}
void cc(int x,int w,int y)
{
    while (x<=L)
    {
        c[y+x]+=w;
        x+=lowbit(x);
    }
}
LL sum(int x,int y)
{
    LL ans=0;
    while (x>0)
    {
        ans+=c[y+x];
        x-=lowbit(x);
    }
    return ans;
}
void link(int x,int y)
{
    dp++;pre[dp]=p[x];p[x]=dp;tt[dp]=y;
}
void gao(int x)
{
    int i;
    i=p[x];
    while (i)
    {
        if (tt[i]!=fa[x])
        {
            fa[tt[i]]=x;
            deep[tt[i]]=deep[x]+1;
            gao(tt[i]);
        }
        i=pre[i];
    }
}
int lca(int x,int y)
{
    if(deep[x]>deep[y])x^=y^=x^=y;
    int i;
    for(i=19;i>=0;i--)
    {
        if(deep[y]-deep[x]>=(1<<i))
        {
            y=ss[y][i];
        }
    }
    if(x==y)return x;
    for(i=19;i>=0;i--)
    {
        if(ss[x][i]!=ss[y][i])
        {
            x=ss[x][i];
            y=ss[y][i];
        }
    }
    return fa[x];
}
void getroot(int x,int fa,int sum)
{
    int i,flag=0;
    i=p[x];s[x]=1;
    while (i)
    {
        if ((!vis[tt[i]])&&(tt[i]!=fa))
        {
            getroot(tt[i],x,sum);
            s[x]+=s[tt[i]];
            if (s[tt[i]]>sum/2) flag=1;
        }
        i=pre[i];
    }
    if (sum-s[x]>sum/2) flag=1;
    if (!flag) tmp=x;
}
void dfs(int x,int fa,int dis)
{
    int i;
    i=p[x];
    if (dis>cnt) cnt=dis;
    v[dis]+=w[x];
    while (i)
    {
        if ((!vis[tt[i]])&&(tt[i]!=fa))
            dfs(tt[i],x,dis+1);
        i=pre[i];
    }
}
void clear()
{
    int i;
    for (i=1;i<=cnt;i++)
    v[i]=0;cnt=0;
}
int work(int x,int fa,int sum)
{
    int i,root,t;
    getroot(x,0,sum);
    root=tmp;
    father[root]=fa;
    i=p[root];
    vis[root]=1;
    while (i)
    {
        if (!vis[tt[i]])
        {
            if (s[root]>s[tt[i]])
            t=work(tt[i],root,s[tt[i]]);
            else
            t=work(tt[i],root,sum-s[root]);
            //------dist(root,point in subtree t)-------- 

            dfs(tt[i],0,2);
            Len[t]=cnt;
            Start[t]=tot;
            for (int j=1;j<=cnt;j++)
            {
                L=cnt;
                cc(j,v[j],Start[t]);
            }
            tot+=cnt;
            clear();

        }
        i=pre[i];
    }
    vis[root]=0;

//--------dist(root,all point)----------

    dfs(root,0,1);
    len[root]=cnt;
    start[root]=tot;
    for (i=1;i<=cnt;i++)
    {
        L=cnt;
        cc(i,v[i],start[root]);
    }
    tot+=cnt;
    clear();

    return root;
}
LL query(int x,int d)
{
    int y=0,z=x,t;
    LL ans=0;
    while (x)
    {
        t=lca(x,z);
        t=deep[x]+deep[z]-2*deep[t];
        L=len[x];
        ans+=sum(min(L,d-t+1),start[x]);

        if (y)
        {
            L=Len[y];
            ans-=sum(min(L,d-t+1),Start[y]);
        }
        y=x;
        x=father[x];
    }
    return ans;
}
void change(int x,int w)
{
    int y=0,z=x,t;
    while (x)
    {
        t=lca(x,z);
        t=deep[x]+deep[z]-2*deep[t];
        L=len[x];
        cc(t+1,w,start[x]);

        if (y)
        {
            L=Len[y];
            cc(t+1,w,Start[y]);
        }
        y=x;
        x=father[x];
    }
}
int main()
{
    while (scanf("%d%d",&n,&m)!=EOF)
    {
    dp=0;memset(p,0,sizeof(p));
    for (i=1;i<=tot;i++)
    c[i]=0;tot=0;

    for (i=1;i<=n;i++)
        scanf("%d",&w[i]);
    for (i=1;i<n;i++)
    {
        scanf("%d%d",&a,&b);
        link(a,b);
        link(b,a);
    }
    gao(1);
    for(i=1;i<=n;i++)
        ss[i][0]=fa[i];
    for(int h=1;h<20;h++)
    {
        for(i=1;i<=n;i++)
        {
            ss[i][h]=ss[ss[i][h-1]][h-1];
        }
    }
    work(1,0,n);

    for (i=1;i<=m;i++)
    {
        getchar();
        char ch;
        scanf("%c%d%d",&ch,&a,&b);
        if (ch==‘?‘)
        printf("%I64d\n",query(a,b));
        else
        {
            change(a,b-w[a]);
            w[a]=b;
        }
    }

    }
}

  

时间: 2024-08-08 15:55:23

hdu4918 Query on the subtree的相关文章

【HDU】4918 Query on the subtree 点分治+树状数组

传送门:[HDU]4918 Query on the subtree 题目分析: 首先,简化问题. 1.求一次到点u的距离不超过d的点的个数.很容易,一次O(NlogN)的点分治便可以完成. 2.多次进行操作1.此时不能每次都O(NlogN)了,太慢了.我们考虑到对于点分治,树的重心一共有logN层,第一层为整棵树的重心,第二层为第一层重心的子树的重心,以此类推,每次至少分成两个大小差不多的子树,所以一共有logN层.而且,对于一个点,他最多只属于logN个子树,也就是最多只属于logN个重心.

HDU 4918 Query on the subtree

Description bobo has a tree, whose vertices are conveniently labeled by 1,2,-,n. At the very begining, the i-th vertex is assigned with weight w i. There are q operations. Each operations are of the following 2 types: Change the weight of vertex v in

hdu 4918

一道比较好的树分治的题目.大力推荐~ Query on the subtree Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 479    Accepted Submission(s): 163 Problem Description bobo has a tree, whose vertices are conveniently

ZOJ 3686 A Simple Tree Problem(线段树)

A Simple Tree Problem Time Limit: 3 Seconds      Memory Limit: 65536 KB Given a rooted tree, each node has a boolean (0 or 1) labeled on it. Initially, all the labels are 0. We define this kind of operation: given a subtree, negate all its labels. An

BNU 28887——A Simple Tree Problem——————【将多子树转化成线段树+区间更新】

A Simple Tree Problem Time Limit: 3000ms Memory Limit: 65536KB This problem will be judged on ZJU. Original ID: 368664-bit integer IO format: %lld      Java class name: Main Prev Submit Status Statistics Discuss Next Type: None None Graph Theory 2-SA

[CF893F]Subtree Minimum Query (主席树)

题面: 传送门:http://codeforces.com/problemset/problem/893/F 题目大意:给你一颗有根树,点有权值,问你每个节点的子树中距离其不超过k的点的权值的最小值.(边权均为1,强制在线) Solution 这题很有意思. 我们一般看到这种距离不超过k的题目,第一反应一般是建以深度为下标,以dfs序为时间轴的的主席树. 很不幸,区间最小值并不能通过减去历史状态得出某个子树的状态. 所以说,这题妙在思想的转换. 考虑以dfs序为下标,以深度为时间轴建一颗主席树.

[hdu 6191] Query on A Tree

Query on A Tree Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others)Total Submission(s): 733    Accepted Submission(s): 275 Problem Description Monkey A lives on a tree, he always plays on this tree. One day, monkey

【bzoj1803】Spoj1487 Query on a tree III DFS序+主席树

题目描述 You are given a node-labeled rooted tree with n nodes. Define the query (x, k): Find the node whose label is k-th largest in the subtree of the node x. Assume no two nodes have the same labels. 输入 The first line contains one integer n (1 <= n <

SQL optimizer -Query Optimizer Deep Dive

refer: http://sqlblog.com/blogs/paul_white/archive/2012/04/28/query-optimizer-deep-dive-part-1.aspx    SQL是一种结构化查询语言规范,它从逻辑是哪个描述了用户需要的结果,而SQL服务器将这个逻辑需求描述转成能执行的物理执行计划,从而把结果返回给用户.将逻辑需求转换成一个更有效的物理执行计划的过程,就是优化的过程. 执行SQL的过程: Input Tree We start by looking