8.11 bzoj1036

Description

  一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I
II. 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
一看就知道是很裸的树剖模板题。这是我第一次写,没看大神模板,全靠自己yy,居然写出来了,感动。
我的做法比较傻逼,对于每次求和/最大值,我都是先算lca,然后把u,v往上提,这就导致了多出了预处理st表的过程以及求lca的过程(话说回来了,这样唯一的好处就是联系了一下lca的模板,对于第一次写lca的我来说也是非常有意义的),在每次求值时,也是分开求得,导致我的代码奇长无比,同时,这种做法的一个潜在弊端就是时间上是o((n+m) logn+n (logn)^2)
来自黄学长的做法,详见http://hzwer.com/2543.html
他的做法与我的做法最大的不同就在于他没有求lca,而是在求和/最大值时将u,v一起往上提,这样一来就节约了求lca的时间,同时代码量大大缩减,这种将两个相似过程合并的奇技淫巧在其他地方也有应用,看来还是得多看模板,多学科技
我的代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>
#include <math.h>
#include <queue>
#include <stack>
#include <map>
#include <vector>
#include <limits.h>
#include <malloc.h>
#include <ctype.h>
#include <float.h>
using namespace std;

const int maxn=30005;
const int oo=1000000005;

int i,n,m,u,v,len,cnt;
int head[maxn];
int deep[maxn];
int a[25];
int w[maxn];
int st[maxn][25];
int f[maxn];
char str[15];
int fa[maxn];
int son[maxn];
int siz[maxn];
int top[maxn];
int o[maxn];

struct edge{
    int u,v,next;
    edge(){u=v=0;next=-1;}
} e[100005];

struct node{
    int l,r,sum,max;
    struct node *lc,*rc;
    node(){l=r=sum=max=0;lc=rc=NULL;}
};
node *cur;

void add_edge(int u,int v){
    len++;
    e[len].u=u;
    e[len].v=v;
    e[len].next=head[u];
    head[u]=len;
}

void new_st(int x,int d,int f){
    deep[x]=d;
    st[x][0]=x;
    st[x][1]=f;
    int i=2;
    while(a[i]<=d){
        st[x][i]=st[st[st[x][i-1]][1]][i-1];
        i++;
    }
    if((e[head[x]].next==-1)&&f)
        return;
    for(i=head[x];i!=-1;i=e[i].next)
        if(e[i].v!=f)
            new_st(e[i].v,d+1,x);
}

int lca(int u,int v){
    if(deep[u]>deep[v])
        swap(u,v);
    int deld=deep[v]-deep[u];
    for(i=23;i>=0;i--)
        if(deld>=a[i]){
            deld-=a[i];
            v=st[st[v][i]][1];
        }
    if(u==v)
        return -1;
    for(i=23;i>=0;i--)
        if(st[st[u][i]][1]!=st[st[v][i]][1]){
            u=st[st[u][i]][1];
            v=st[st[v][i]][1];
        }
    return st[u][1];
}

node* new_tree(int l,int r){
    node *p=new node;
    p->l=l;
    p->r=r;
    if(l==r){
        p->sum=p->max=w[f[l]];
        return p;
    }
    p->lc=new_tree(l,(l+r)/2);
    p->rc=new_tree((l+r)/2+1,r);
    p->sum=p->lc->sum+p->rc->sum;
    p->max=max(p->lc->max,p->rc->max);
    return p;
}

void print(node *p){
    printf("l=%d r=%d:sum=%d max=%d\n",p->l,p->r,p->sum,p->max);
    if(p->l!=p->r){
        print(p->lc);
        print(p->rc);
    }
}

int q_sum(node *p,int l,int r){
    if(p->l>=l&&p->r<=r)
        return p->sum;
    if(p->l==p->r)
        return p->sum;
    int tmp=0;
    if(l<=p->lc->r)
        tmp+=q_sum(p->lc,l,r);
    if(r>=p->rc->l)
        tmp+=q_sum(p->rc,l,r);
    return tmp;
}

int q_max(node *p,int l,int r){
    if(p->l>=l&&p->r<=r)
        return p->max;
    if(p->l==p->r)
        return p->max;
    int tmp=-oo;
    if(l<=p->lc->r)
        tmp=max(tmp,q_max(p->lc,l,r));
    if(r>=p->rc->l)
        tmp=max(tmp,q_max(p->rc,l,r));
    return tmp;
}

void change(node *p,int x,int z){
    if(p->l==p->r){
        p->sum=p->max=z;
        return;
    }
    if(x<=p->lc->r)
        change(p->lc,x,z);
    else
        change(p->rc,x,z);
    p->sum=p->lc->sum+p->rc->sum;
    p->max=max(p->lc->max,p->rc->max);
}

int dfs1(int x,int f){
    int ma=0,mi=-1,t;
    fa[x]=f;
    for(int i=head[x];i!=-1;i=e[i].next)
        if(e[i].v!=f){
            t=dfs1(e[i].v,x);
            siz[x]+=t;
            if(t>ma){
                ma=t;
                mi=e[i].v;
            }
        }
    son[x]=mi;
    siz[x]++;
    return siz[x];
}

void dfs2(int x,int flag){
    cnt++;
    f[cnt]=x;
    o[x]=cnt;
    if(flag)
        top[x]=top[fa[x]];
    else
        top[x]=x;
    if(son[x]==-1)
        return;
    dfs2(son[x],1);
    for(int i=head[x];i!=-1;i=e[i].next)
        if(e[i].v!=son[x]&&e[i].v!=fa[x])
            dfs2(e[i].v,0);
}

void work_sum(){
    int tmp=0,zx;
    if((zx=lca(u,v))==-1){
        if(deep[u]>deep[v])
            swap(u,v);
        while(top[u]!=top[v]){
            tmp+=q_sum(cur,o[top[v]],o[v]);
            v=fa[top[v]];
        }
        tmp+=q_sum(cur,o[u],o[v]);
        printf("%d\n",tmp);
        return;
    }
    while(top[zx]!=top[u]){
        tmp+=q_sum(cur,o[top[u]],o[u]);
        u=fa[top[u]];
    }
    tmp+=q_sum(cur,o[zx],o[u]);
    while(top[zx]!=top[v]){
        tmp+=q_sum(cur,o[top[v]],o[v]);
        v=fa[top[v]];
    }
    tmp+=q_sum(cur,o[zx],o[v]);
    tmp-=q_sum(cur,o[zx],o[zx]);
    printf("%d\n",tmp);
}

void work_max(){
    int tmp=-oo,zx;
    if((zx=lca(u,v))==-1){
        if(deep[u]>deep[v])
            swap(u,v);
        while(top[u]!=top[v]){
            tmp=max(tmp,q_max(cur,o[top[v]],o[v]));
            v=fa[top[v]];
        }
        tmp=max(tmp,q_max(cur,o[u],o[v]));
        printf("%d\n",tmp);
        return;
    }
    while(top[zx]!=top[u]){
        tmp=max(tmp,q_max(cur,o[top[u]],o[u]));
        u=fa[top[u]];
    }
    tmp=max(tmp,q_max(cur,o[zx],o[u]));
    while(top[zx]!=top[v]){
        tmp=max(tmp,q_max(cur,o[top[v]],o[v]));
        v=fa[top[v]];
    }
    tmp=max(tmp,q_max(cur,o[zx],o[v]));
    printf("%d\n",tmp);
}

int main(){
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        head[i]=-1;
    for(i=1,a[0]=1;i<=23;i++)
        a[i]=a[i-1]*2;
    for(i=1;i<n;i++){
        scanf("%d %d",&u,&v);
        add_edge(u,v);
        add_edge(v,u);
    }
    new_st(1,1,0);
    for(i=1;i<=n;i++)
        scanf("%d",&w[i]);
    dfs1(1,0);
    cnt=0;
    dfs2(1,0);
    cur=new_tree(1,n);
    scanf("%d\n",&m);
    while(m--){
        scanf("%s %d %d\n",str,&u,&v);
        switch(str[1]){
            case 'H':change(cur,o[u],v);break;
            case 'M':work_max();break;
            case 'S':work_sum();break;
        }
    }
    return 0;
}

大神模板

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#define inf 0x7fffffff
#define N 30005 
#define M 60005
using namespace std;
int n,q,cnt,sz;
int v[N],dep[N],size[N],head[N],fa[N];
int pos[N],bl[N];
struct data{int to,next;}e[M];
struct seg{int l,r,mx,sum;}t[100005];
void insert(int u,int v)
{
    e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;
    e[++cnt].to=u;e[cnt].next=head[v];head[v]=cnt;
}
void ini()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        insert(x,y);
    }
    for(int i=1;i<=n;i++)scanf("%d",&v[i]);
}
void dfs1(int x)
{
    size[x]=1;
    for(int i=head[x];i;i=e[i].next)
    {
        if(e[i].to==fa[x])continue;
        dep[e[i].to]=dep[x]+1;
        fa[e[i].to]=x;
        dfs1(e[i].to);
        size[x]+=size[e[i].to];
    }
}
void dfs2(int x,int chain)
{
    int k=0;sz++;
    pos[x]=sz;//分配x结点在线段树中的编号
    bl[x]=chain;
    for(int i=head[x];i;i=e[i].next)
        if(dep[e[i].to]>dep[x]&&size[e[i].to]>size[k])
            k=e[i].to;//选择子树最大的儿子继承重链
    if(k==0)return;
    dfs2(k,chain);
    for(int i=head[x];i;i=e[i].next)
        if(dep[e[i].to]>dep[x]&&k!=e[i].to)
            dfs2(e[i].to,e[i].to);//其余儿子新开重链
}
void build(int k,int l,int r)//建线段树
{
    t[k].l=l;t[k].r=r;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
}
void change(int k,int x,int y)//线段树单点修改
{
    int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
    if(l==r){t[k].sum=t[k].mx=y;return;}
    if(x<=mid)change(k<<1,x,y);
    else change(k<<1|1,x,y);
    t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
    t[k].mx=max(t[k<<1].mx,t[k<<1|1].mx);
}
int querysum(int k,int x,int y)//线段树区间求和
{
    int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
    if(l==x&&y==r)return t[k].sum;
    if(y<=mid)return querysum(k<<1,x,y);
    else if(x>mid)return querysum(k<<1|1,x,y);
    else {return querysum(k<<1,x,mid)+querysum(k<<1|1,mid+1,y);}
}
int querymx(int k,int x,int y)//线段树区间求最大值
{

    int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
    if(l==x&&y==r)return t[k].mx;
    if(y<=mid)return querymx(k<<1,x,y);
    else if(x>mid)return querymx(k<<1|1,x,y);
    else {return max(querymx(k<<1,x,mid),querymx(k<<1|1,mid+1,y));}
}
int solvesum(int x,int y)
{
    int sum=0;
    while(bl[x]!=bl[y])
    {
        if(dep[bl[x]]<dep[bl[y]])swap(x,y);
        sum+=querysum(1,pos[bl[x]],pos[x]);
        x=fa[bl[x]];
    }
    if(pos[x]>pos[y])swap(x,y);
    sum+=querysum(1,pos[x],pos[y]);
    return sum;
}
int solvemx(int x,int y)
{
    int mx=-inf;
    while(bl[x]!=bl[y])
    {
        if(dep[bl[x]]<dep[bl[y]])swap(x,y);
        mx=max(mx,querymx(1,pos[bl[x]],pos[x]));
        x=fa[bl[x]];
    }
    if(pos[x]>pos[y])swap(x,y);
    mx=max(mx,querymx(1,pos[x],pos[y]));
    return mx;
}
void solve()
{
    build(1,1,n);
    for(int i=1;i<=n;i++)
        change(1,pos[i],v[i]);
    scanf("%d",&q);
    char ch[10];
    for(int i=1;i<=q;i++) 
    {
        int x,y;scanf("%s%d%d",ch,&x,&y);
        if(ch[0]=='C'){v[x]=y;change(1,pos[x],y);}
        else
        {
            if(ch[1]=='M')
               printf("%d\n",solvemx(x,y));
            else
                printf("%d\n",solvesum(x,y));
        }
    }
}
int main()
{
    ini();
    dfs1(1);
    dfs2(1,1);
    solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值