码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj3306: 树(dfs序+倍增+线段树)

时间:2017-12-08 22:53:06      阅读:147      评论:0      收藏:0      [点我收藏+]

标签:etc   i++   ++   分类   include   std   UI   技术分享   ==   

  比较傻逼的一道题...

  显然求子树最小值就是求出dfs序用线段树维护嘛

  换根的时候树的形态不会改变,所以我们可以根据相对于根的位置分类讨论。

  如果询问的x是根就直接输出整棵树的最小值。

  如果询问的x是根在原树上的子节点,直接输出子树的最小值。

  如果询问的x是根在原树上的祖先,那么就要输出整棵树去掉x在原树上那个包含根的子节点的子树的答案。

  至于怎么求x在原树上那个包含根的子节点,可以用倍增。

  但是我发现直接遍历x的子节点居然不会被卡,而且还跑到了第一页嘿嘿嘿

技术分享图片
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
const int maxn=500010, inf=1e9;
struct poi{int too, pre;}e[maxn];
struct tjm{int sum;}tree[maxn<<2];
int n, q, x, y, tot, tott, root;
int a[maxn], last[maxn], l[maxn], r[maxn], pos[maxn], d[maxn], f[maxn][20];
char s[2];
inline void read(int &k)
{
    int f=1; k=0; char c=getchar();
    while(c<0 || c>9) c==- && (f=-1), c=getchar();
    while(c<=9 && c>=0) k=k*10+c-0, c=getchar();
    k*=f;
}
inline void add(int x, int y){e[++tot].too=y; e[tot].pre=last[x]; last[x]=tot;}
void dfs(int x)
{
    l[x]=++tott; pos[tott]=x; d[x]=d[f[x][0]]+1;
    for(int i=last[x];i;i=e[i].pre) f[e[i].too][0]=x, dfs(e[i].too);
    r[x]=tott;
}
inline int min(int a, int b) {return a<b?a:b;}
inline void up(int x) {tree[x].sum=min(tree[x<<1].sum, tree[x<<1|1].sum);}
void build(int x, int l, int r)
{
    if(l==r) {tree[x].sum=pos[l]?a[pos[l]]:inf; return;}
    int mid=(l+r)>>1;
    build(x<<1, l, mid); build(x<<1|1, mid+1, r);
    up(x);
}
void update(int x, int l, int r, int cx, int delta)
{
    if(l==r) {tree[x].sum=delta; return;}
    int mid=(l+r)>>1;
    if(cx<=mid) update(x<<1, l, mid, cx, delta);
    else update(x<<1|1, mid+1, r, cx, delta);
    up(x);
}
int query(int x, int l, int r, int cl, int cr)
{
    if(cl>cr) return inf;
    if(cl<=l && r<=cr) return tree[x].sum;
    int mid=(l+r)>>1, ans=inf;
    if(cl<=mid) ans=query(x<<1, l, mid, cl, cr);
    if(cr>mid) ans=min(ans, query(x<<1|1, mid+1, r, cl, cr));
    return ans;
}
inline int solve(int x)
{
    if(x==root) return tree[1].sum;
    if(l[x]>l[root] || r[x]<l[root]) return query(1, 1, n, l[x], r[x]);
    int now=root;
    for(int i=16;i>=0;i--) if(d[f[now][i]]>x) now=f[now][i];
    return min(query(1, 1, n, 1, l[now]-1), query(1, 1, n, r[now]+1, n));
}
int main()
{
    read(n); read(q);
    for(int i=1;i<=n;i++)
    {
        read(x); read(a[i]);
        if(x) add(x, i);
    }
    dfs(root=1); build(1, 1, n);
    for(int j=1;j<=16;j++) for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1];
    for(int i=1;i<=q;i++)
    {
        scanf("%s", s+1);
        if(s[1]==V) read(x), read(y), update(1, 1, n, l[x], y);
        else if(s[1]==E) read(root);
        else read(x), printf("%d\n", solve(x));
    }
}
View Code

 

bzoj3306: 树(dfs序+倍增+线段树)

标签:etc   i++   ++   分类   include   std   UI   技术分享   ==   

原文地址:http://www.cnblogs.com/Sakits/p/8006985.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!