[BZOJ1036][ZJOI2008]树的统计Count

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

这是一道较水的码农题,我的作法是树链剖分套zkw线段树,我们沿着重链的路径建立线段树,最多有 log(n) 条重链,线段树对每条链询问,所以一次询问是 O(log(n)2) 的,所以总的复杂度是 O(qlog(n)2) 可以轻松过掉。
注意点:答案有负数,max询问初始的res不能是0
码农题挂了多多debug,断点输出,自己随机小数据,就能找到原因了
树上的就考虑链,菊花等数据debug一下,比如刚开始我的代码漏洞也很多,于是满满的debug

代码:

/**************************************************************
    Problem: 1036
    User: waz
    Language: C++
    Result: Accepted
    Time:1900 ms
    Memory:4032 kb
****************************************************************/

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define cut_print puts("All of is all right")
//树链剖分套zkw线段树 
namespace work_space{
    const int maxn=30010;
    int v[maxn];
    int siz[maxn],fa[maxn],dep[maxn],son[maxn];
    int dfn[maxn],top[maxn],timer;
    //树链剖分 
    struct edge{int to;edge*next;}*last[maxn],e[maxn<<1],*et=e;
    void add(int u,int v){
        *++et=(edge){v,last[u]},last[u]=et;
        *++et=(edge){u,last[v]},last[v]=et;
    }
    void dfs1(int u){
        dep[u]=dep[fa[u]]+1;
        siz[u]=1;
        for(edge*it=last[u];it;it=it->next)
            if(fa[u]!=it->to){
                fa[it->to]=u;
                dfs1(it->to);
                siz[u]+=siz[it->to];
                if(siz[it->to]>siz[son[u]])
                    son[u]=it->to;
            }
    }
    void dfs2(int u,int tp){
        top[u]=tp;
        dfn[u]=++timer;
        if(son[u])dfs2(son[u],tp);
        for(edge*it=last[u];it;it=it->next)
            if(fa[it->to]==u&&son[u]!=it->to)
                dfs2(it->to,it->to);
    }
    //根据dfs序建立zkw线段树
    int seg_max[maxn<<2],seg_sum[maxn<<2],cnt;
    void update(int pos){
        seg_max[pos]=std::max(seg_max[pos<<1],seg_max[pos<<1|1]);
        seg_sum[pos]=seg_sum[pos<<1]+seg_sum[pos<<1|1];
    }
    void init(int n){
        for(cnt=1;cnt<=n+1;cnt<<=1);cnt--;
        for(int i=1;i<=n;i++)seg_max[dfn[i]+cnt]=seg_sum[dfn[i]+cnt]=v[i];
        for(int i=cnt;i;i--)update(i);
    }
    void modity(int pos,int x){
        //printf("pos = %d x = %d\n",pos,x);
        v[pos]=x;pos=dfn[pos]+cnt;
        seg_max[pos]=seg_sum[pos]=x;
        for(pos>>=1;pos;pos>>=1)update(pos);
    }
    int query_sum(int s,int t){
        s=dfn[s],t=dfn[t];
        if(s>t)std::swap(s,t);
        int res=0;
        for(s+=cnt-1,t+=cnt+1;s^t^1;s>>=1,t>>=1)
            s&1?:res+=seg_sum[s^1],t&1?res+=seg_sum[t^1]:1;
        return res;
    }
    int query_max(int s,int t){
        s=dfn[s],t=dfn[t];
        if(s>t)std::swap(s,t);
        int res=-1<<30;
        for(s+=cnt-1,t+=cnt+1;s^t^1;s>>=1,t>>=1)
            s&1?:res=std::max(seg_max[s^1],res),t&1?res=std::max(res,seg_max[t^1]):1;
        return res;
    }
    int qmax(int u,int v){
        int ans=-1<<30;
        for(;top[u]!=top[v];){
            if(dep[top[u]]>dep[top[v]]){
                ans=std::max(ans,query_max(u,top[u]));
                u=fa[top[u]];
            } else {
                ans=std::max(ans,query_max(v,top[v]));
                v=fa[top[v]];
            }
        }
        ans=std::max(ans,query_max(u,v));
        return ans;
    }
    int qsum(int u,int v){
        int ans=0;
        for(;top[u]!=top[v];){
            //printf("(u, v) = (%d, %d)\n",u,v);
            if(dep[top[u]]>dep[top[v]]){
                ans+=query_sum(u,top[u]);
                u=fa[top[u]];
            } else {
                ans+=query_sum(v,top[v]);
                v=fa[top[v]];
            }
        }
        ans+=query_sum(u,v);
        return ans;
    }
    int n,q;
    char op[10];
    void main(){
        scanf("%d",&n);
        for(int i=1;i<n;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);//
        }
        for(int i=1;i<=n;i++)scanf("%d",v+i);
        dfs1(1),dfs2(1,1),init(n);
        //for(int i=1;i<=n;i++)printf("i = %d , fa = %d , top = %d\n",i,fa[i],top[i]);
        scanf("%d",&q);
        for(;q--;){
            scanf("%s",op);
            int o;
            if(!strcmp(op,"QMAX"))o=1;
            else if(!strcmp(op,"QSUM"))o=2;
            else o=3;
            if(o==1){
                int u,v;
                scanf("%d%d",&u,&v);
                //printf("top[%d] = %d , top[%d] = %d\n",u,top[u],v,top[v]);
                printf("%d\n",qmax(u,v));
                //OK!
            } else if(o==2) {
                int u,v;
                scanf("%d%d",&u,&v);
                printf("%d\n",qsum(u,v));
            } else {
                int pos,x;
                scanf("%d%d",&pos,&x);//cut_print;
                modity(pos,x);
            }
        } 
    }
}
#define set_file(x) freopen(#x".in","r",stdin),freopen("my_"#x".out","w",stdout)
int main(){
    //set_file(count1);
    work_space::main();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值