动态树||树链剖分(BZOJ1036)

1036: [ZJOI2008]树的统计Count

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 7051   Solved: 2872
[ Submit][ Status][ Discuss]

Description

一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身

Input

输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。

Output

对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

Sample Input

4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4

Sample Output

4
1
2
2
10
6
5
6
5
16

HINT

Source


思路:因为操作的是点权。所以可以直接把其中一个变成根,然后接着搞,要是边权的话,就不行了,就要按照LCA那样高了

这个题也可以用树链剖分写



#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=30010;
const int INF=1000000000;
int N,Q;
struct node
{
    int v,next;
}edge[maxn*2];
int head[maxn];
int tot;
void add_edge(int u,int v)
{
    edge[tot].v=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
struct LCT
{
    int ch[maxn][2],pre[maxn],key[maxn];
    int rev[maxn],maxv[maxn];
    bool rt[maxn];
    int sum[maxn];

    void update_rev(int r)
    {
        if(!r)return;
        swap(ch[r][0],ch[r][1]);
        rev[r]^=1;
    }
    void pushdown(int r)
    {

        if(rev[r])
        {
            update_rev(ch[r][1]);
            update_rev(ch[r][0]);
            rev[r]=0;
        }
    }
    void pushup(int r)
    {
        maxv[r]=max(max(maxv[ch[r][0]],maxv[ch[r][1]]),key[r]);
        sum[r]=sum[ch[r][0]]+sum[ch[r][1]]+key[r];
    }
    void rotate(int x)
    {
        int y=pre[x],kind=ch[y][1]==x;
        ch[y][kind]=ch[x][!kind];
        pre[ch[y][kind]]=y;
        pre[x]=pre[y];
        pre[y]=x;
        ch[x][!kind]=y;
        if(rt[y])rt[y]=false,rt[x]=true;
        else ch[pre[x]][ch[pre[x]][1]==y]=x;
        pushup(y);
    }
    //将根节点到r的路径上的所有及诶单的标记下方
    void P(int r)
    {
        if(!rt[r])P(pre[r]);
        pushdown(r);
    }
    void Splay(int r)
    {
        P(r);
        while(!rt[r])
        {
            int f=pre[r],ff=pre[f];
            if(rt[f])rotate(r);
            else if((ch[ff][1]==f)==(ch[f][1]==r))
                rotate(f),rotate(r);
            else rotate(r),rotate(r);
        }
        pushup(r);
    }
    int Access(int x)
    {
        int y=0;
        for(;x;x=pre[y=x])
        {
            Splay(x);
            rt[ch[x][1]]=true,rt[ch[x][1]=y]=false;
            pushup(x);
        }
        return y;
    }
     int getroot(int x)
     {
        Access(x);
        Splay(x);
        while (ch[x][0])
            x = ch[x][0];
        return x;
    }
    //判断是否同根
    bool judge(int u,int v)
    {
        while(pre[u])u=pre[u];
        while(pre[v])v=pre[v];
        return u==v;
    }
    //将r变成他所在根
    void mroot(int r)
    {
        Access(r);
        Splay(r);
        update_rev(r);
    }
    //调用后u是原来u和v的lca,v和ch[u][1]分别存折lca的两个儿子
    void lca(int &u,int &v)
    {
        Access(v),v=0;
        while(u)
        {
            Splay(u);
            if(!pre[u])return;
            rt[ch[u][1]]=true;
            rt[ch[u][1]=v]=false;
            pushup(u);
            u=pre[v=u];
        }
    }
    //将u合并到v上
    void link(int u,int v)
    {
        mroot(u);
        pre[u]=v;
    }
    //将v和他的父节点分离
    void cut(int v)
    {
        //mroot(v);
        Access(v);
        Splay(v);
        pre[ch[v][0]]=0;
        pre[v]=0;
        rt[ch[v][0]]=true;
        ch[v][0]=0;
        pushup(v);
    }
    void init()
    {
        memset(head,-1,sizeof(head));
        memset(pre,0,sizeof(pre));
        memset(ch,0,sizeof(ch));
        memset(rev,0,sizeof(rev));
        memset(sum,0,sizeof(sum));
        for(int i=0;i<=N;i++)rt[i]=true;
        maxv[0]=-INF;
    }
    void dfs(int u,int f)
    {
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(v==f)continue;
            pre[v]=u;
            dfs(v,u);
        }
    }
}tree;
int main()
{
    int u,v;
    char op[10];
    while(scanf("%d",&N)!=EOF)
    {
        tree.init();
        for(int i=1;i<N;i++)
        {
            scanf("%d%d",&u,&v);
            add_edge(u,v);
            add_edge(v,u);
        }
        for(int i=1;i<=N;i++)scanf("%d",&tree.key[i]);
        tree.dfs(1,0);
        scanf("%d",&Q);
        while(Q--)
        {
            scanf("%s%d%d",op,&u,&v);//cout<<op<<endl;
            if(op[0]=='C')
            {
                tree.Access(u);
                tree.Splay(u);
                tree.key[u]=v;
                tree.pushup(u);
            }
            else if(op[1]=='M')
            {
                //注意一下三个操作的顺序
                tree.mroot(u);
                tree.Access(v);
                tree.Splay(u);
                printf("%d\n",tree.maxv[u]);
            }
            else
            {
                tree.mroot(u);
                tree.Access(v);
                tree.Splay(u);
                //tree.Splay(v);
                printf("%d\n",tree.sum[u]);
            }
        }
    }
    return 0;
}

树链剖分写发(转自 http://www.cnblogs.com/silver-bullet/archive/2013/02/09/2909473.html):

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 30010
#define lson l,m,n<<1
#define rson m+1,r,n<<1|1
using namespace std;
const int inf=1<<30;
struct Edge{
    int u,v,next;
    Edge(){}
    Edge(int _u,int _v,int _next){
        u=_u;v=_v;next=_next;
    }
}edge[N<<1];
int head[N],cnt;
int sz[N],top[N],fa[N],dep[N],hash[N],w[N],son[N],num;
void init(){
    memset(head,-1,sizeof(head));
    cnt=num=0;
}
void add(int u,int v){
    edge[cnt]=Edge(u,v,head[u]);head[u]=cnt++;
    edge[cnt]=Edge(v,u,head[v]);head[v]=cnt++;
}
void dfs(int u,int d){
    sz[u]=1;son[u]=0;dep[u]=d;
    for(int k=head[u];k!=-1;k=edge[k].next){
        int v=edge[k].v;
        if(v==fa[u])continue;
        fa[v]=u;
        dfs(v,d+1);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]])son[u]=v;
    }
}
void build_tree(int u,int pre){
    hash[u]=++num;top[u]=pre;
    if(son[u])build_tree(son[u],pre);
    for(int k=head[u];k!=-1;k=edge[k].next){
        int v=edge[k].v;
        if(v!=fa[u]&&v!=son[u])build_tree(v,v);
    }
}
struct segtree{
    int maxn[N<<2],sum[N<<2];
    void pushup(int n){
        sum[n]=sum[n<<1]+sum[n<<1|1];
        maxn[n]=max(maxn[n<<1],maxn[n<<1|1]);
    }
    void update(int nn,int x,int l,int r,int n){
        if(l==r){
            maxn[n]=sum[n]=x;
            return;
        }
        int m=(l+r)>>1;
        if(nn<=m)update(nn,x,lson);
        else update(nn,x,rson);
        pushup(n);
    }
    int query_max(int ll,int rr,int l,int r,int n){
        if(ll==l&&rr==r)return maxn[n];
        int m=(l+r)>>1;
        if(rr<=m)return query_max(ll,rr,lson);
        else if(ll>m)return query_max(ll,rr,rson);
        else return max(query_max(ll,m,lson),query_max(m+1,rr,rson));
    }
    int query_sum(int ll,int rr,int l,int r,int n){
        if(ll==l&&rr==r)return sum[n];
        int m=(l+r)>>1;
        if(rr<=m)return query_sum(ll,rr,lson);
        else if(ll>m)return query_sum(ll,rr,rson);
        else return query_sum(ll,m,lson)+query_sum(m+1,rr,rson);
    }
}seg;
int Query_max(int a,int b,int n){
    int ta=top[a],tb=top[b],ans=-inf;
    while(ta!=tb){
        if(dep[ta]<dep[tb]){
            swap(ta,tb);swap(a,b);
        }
        ans=max(ans,seg.query_max(hash[ta],hash[a],1,n,1));
        a=fa[ta];ta=top[a];
    }
    if(dep[a]>dep[b])swap(a,b);
    return max(ans,seg.query_max(hash[a],hash[b],1,n,1));
}
int Query_sum(int a,int b,int n){
    int ta=top[a],tb=top[b],ans=0;
    while(ta!=tb){
        if(dep[ta]<dep[tb]){
            swap(ta,tb);swap(a,b);
        }
        ans+=seg.query_sum(hash[ta],hash[a],1,n,1);
        a=fa[ta];ta=top[a];
    }
    if(dep[a]>dep[b])swap(a,b);
    return ans+seg.query_sum(hash[a],hash[b],1,n,1);
}
int main(){
    int n,a,b,q;
    char op[10];
    while(~scanf("%d",&n)){
        init();
        for(int i=1;i<n;i++){
            scanf("%d%d",&a,&b);
            add(a,b);
        }
        for(int i=1;i<=n;i++)scanf("%d",&w[i]);
        dfs(1,1);
        build_tree(1,1);
        for(int i=1;i<=n;i++)seg.update(hash[i],w[i],1,n,1);
        scanf("%d",&q);
        while(q--){
            scanf("%s%d%d",op,&a,&b);
            if(strcmp(op,"QMAX")==0){
                int ans=Query_max(a,b,n);
                printf("%d\n",ans);
            }else if(strcmp(op,"QSUM")==0){
                int ans=Query_sum(a,b,n);
                printf("%d\n",ans);
            }else {
                seg.update(hash[a],b,1,n,1);
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值