BZOJ 3306 树

dfs序建线段树+分类讨论+写的有点长。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxv 100500
#define maxe 200500
#define inf 2147483646
using namespace std;
int n,m,w[maxv],dfn[maxv],fdfn[maxv],g[maxv],nume=0,x,y,cnt=0,dis[maxv],mx[maxv],anc[maxv][23];
int tot=0,root,ls[maxv<<2],rs[maxv<<2],val[maxv<<2],nrt;
char s[4];
struct edge
{
    int v,nxt;
}e[maxe];
void addedge(int u,int v)
{
    e[++nume].v=v;
    e[nume].nxt=g[u];
    g[u]=nume;
}
void dfs(int x,int fath)
{
    dfn[x]=++cnt;mx[x]=dfn[x];fdfn[cnt]=x;
    for (int i=g[x];i;i=e[i].nxt)
    {
        int v=e[i].v;
        if (v==fath) continue;
        dis[v]=dis[x]+1;
        dfs(v,x);anc[v][0]=x;
        mx[x]=max(mx[x],mx[v]);
    }
}
void get_table()
{
    for (int e=1;e<=20;e++)
        for (int i=1;i<=n;i++)
            anc[i][e]=anc[anc[i][e-1]][e-1];
}
void build(int &now,int left,int right)
{
    now=++tot;
    if (left==right)
    {
        val[now]=w[fdfn[left]];
        return;
    }
    int mid=(left+right)>>1;
    build(ls[now],left,mid);
    build(rs[now],mid+1,right);
    val[now]=min(val[ls[now]],val[rs[now]]);
}
void modify(int now,int left,int right,int pos)
{
    if ((left==right) && (left==pos))
    {
        val[now]=w[fdfn[left]];
        return;
    }
    int mid=(left+right)>>1;
    if (pos<=mid) modify(ls[now],left,mid,pos);
    else modify(rs[now],mid+1,right,pos);
    val[now]=min(val[ls[now]],val[rs[now]]);
}
int ask(int now,int left,int right,int l,int r)
{
    if (l>r) return inf;
    if ((l<=0) || (r>=n+1)) return inf;
    if ((left==l) && (right==r)) return val[now];
    int mid=(left+right)>>1;
    if (r<=mid) return ask(ls[now],left,mid,l,r);
    else if (l>=mid+1) return ask(rs[now],mid+1,right,l,r);
    else return min(ask(ls[now],left,mid,l,mid),ask(rs[now],mid+1,right,mid+1,r));
}
int find(int x,int pos)
{
    for (int e=20;e>=0;e--)
    {
        if (dis[anc[x][e]]>dis[pos])
            x=anc[x][e];
    }
    return x;
}
int main()
{
    freopen("cin.in","r",stdin);
    freopen("a.out","w",stdout);
    nrt=1;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
    {
        scanf("%d%d",&x,&w[i]);
        if (x!=0) {addedge(i,x);addedge(x,i);}
    }
    dfs(1,1);
    get_table();
    build(root,1,n);
    for (int i=1;i<=m;i++)
    {
        scanf("%s",s);
        if (s[0]==‘V‘)
        {
            scanf("%d%d",&x,&y);
            w[x]=y;
            modify(root,1,n,dfn[x]);
        }
        else if (s[0]==‘E‘) scanf("%d",&nrt);
        else
        {
            scanf("%d",&x);
            if (x==nrt) printf("%d\n",val[1]);
            else if ((dfn[nrt]>=dfn[x]) && (dfn[nrt]<=mx[x]))
            {
                int r=find(nrt,x);
                printf("%d\n",min(ask(root,1,n,1,dfn[r]-1),ask(root,1,n,mx[r]+1,n)));
            }
            else printf("%d\n",ask(root,1,n,dfn[x],mx[x]));
        }
    }
    return 0;
}
时间: 2024-10-01 06:17:18

BZOJ 3306 树的相关文章

[BZOJ 3306]树(dfs序+线段树+倍增)

Description 给定一棵大小为 n 的有根点权树,支持以下操作: • 换根 • 修改点权 • 查询子树最小值 Solution 单点修改子树查询的话可以想到用dfs序+线段树来处理,换根的处理画一画图应该可以明白: 如果查询的x是当前的根rt,直接返回整棵树的min 如果rt在x的子树中,用倍增的方法找到离x最近的rt的祖先t,整棵树除t的子树以外的部分就是x当前根下的子树 如果rt不在x的子树中,查询x原来的子树的min值 #include<iostream> #include<

bzoj 3083 树链剖分

首先我们先将树提出一个根变成有根树,那么我们可以通过树链剖分来实现对于子树的最小值求解,那么按照当前的根和询问的点的相对位置关系我们可以将询问变成某个子树和或者除去某颗子树之后其余的和,前者直接询问区间,后者询问区间的补集. /************************************************************** Problem: 3083 User: BLADEVIL Language: C++ Result: Accepted Time:6412 ms

BZOJ 1036 树链剖分模板题

BZOJ 1036 题意:一棵树,每个点有权值,三种操作:修改一个点的值:询问一条链上最大值:询问一条链上权值和. tags:模板题 // bzoj 1036 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define FF(i,a,b) for (int i=a;i<=b;i++) #define F(i,b,a)

bzoj 3306

以1号节点为根,弄出DFS序,我们发现,对于一个询问:(rt,u),以rt为根,u节点的子树中的最小点权,我们可以根据rt,u,1这三个节点在同一条路径上的相对关系来把它转化为以1为根的在DFS序上的区间询问(中间有一种情况要在树上倍增,理解了LCA的话应该很容易写出来). 收获: 对于只有换根这种改变树的形态的操作,又只询问和子树相关的问题,可以不用动态树. 1 /************************************************************** 2

spoj 375 AND bzoj 1036 树链剖分

树链剖分的入门题目,自己写了下感觉还是挺好写的,不过真的有点长... spoj 375 边有权值: 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 6 const int INF = -999999999; 7 const int N = 10001; 8 int head[N]; 9 int sz[N]; 10 int depth[N]; 1

bzoj 3196 树套树模板

然而我还是在继续刷水题... 终于解开了区间第k大的心结... 比较裸的线段树套平衡树,比较不好想的是求区间第k大时需要二分一下答案,然后问题就转化为了第一个操作.复杂度nlog3n.跑的比较慢... 在查前驱后继的时候写错了...如果要直接赋值ans的话前驱是k[x]<=z,后继是k[x]<z,如果都写<的话需要取max和min...(不是第一次犯这种错了) 1 #include<iostream> 2 #include<cstdio> 3 #include&l

BZOJ 2282 &amp; 树的直径

SDOI2011的Dayx第2题 题意: 在树中找到一条权值和不超过S的链(为什么是链呢,因为题目中提到“使得路径的两端都是城市”,如果不是链那不就不止两端了吗——怎么这么机智的感觉...),使得不在链上的点与这条链的距离最大值最小. SOL: 最大值最小!这不是二分的节奏么?然而hzw学长说二分更直观我却一点都没有体会到... 这道题的关键是猜想(貌似还挺好想)并证明(貌似一直都是可有可无的东西,不过还挺好证的),路径一定在直径上,那么我们先两遍*FS找到直径,用一个队列维护链上的路径,以及预

BZOJ 1036 树的统计(树链剖分)

PS:树链剖分的很基本的题 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define rep(i, a, b) for (int i(a); i <= (b); ++i) 6 #define dec(i, a, b) for (int i(a); i >= (b); --i) 7 #define lson i << 1, L, mid 8 #define rson i << 1 | 1,

BZOJ 1907 树的路径覆盖 树形DP

题目大意:给定一棵树,求最小路径覆盖 数据范围1W,看到还想跑网络流来着= = 不过算了明明树形DP这么水还是不要用网络流这种大杀器为好 首先将所有的链都考虑成以链上所有点的LCA为转折点的V字形 那么点有两种:转折点和非转折点 因此我们选择两种状态进行转移:还会和父亲组成链的状态和成为转折点的状态 转移就自己YY算了 时间复杂度是线性的 #include <cstdio> #include <cstring> #include <iostream> #include