【BZOJ 1036】树的统计Count(树链剖分)

【BZOJ 1036】树的统计Count(树链剖分)

1036: [ZJOI2008]树的统计Count

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 12991   Solved: 5233

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

树剖板子题,手生了居然在线段树挂了=。=GG……

顺发现一个小东西,查询区间的时候其实不用按链顶的深度来查。
按dfs序就行。

代码如下:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define fread(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 10000;
const int mod = 1e9+7;
const double eps = 1e-8;

struct Edge
{
    int v,next;
};

int bit[2][233333];
int head[33333];
int ttp[33333],dfn[33333],nxt[33333],pre[33333];
Edge eg[66666];
int tp,n,tim;

void Add(int u,int v)
{
    eg[tp].v = v;
    eg[tp].next = head[u];
    head[u] = tp++;
}

int dfs1(int u,int pre)
{
    int v;
    int mx = 0,id,tmp,cnt;
    id = u;
    cnt = 1;

    for(int i = head[u]; i != -1; i = eg[i].next)
    {
        v = eg[i].v;
        if(v == pre) continue;
        tmp = dfs1(v,u);
        cnt += tmp;
        //printf("%d-%d %d\n",u,v,tmp);
        if(mx < tmp)
        {
            mx = tmp;
            id = v;
        }
    }

    nxt[u] = id;
    return cnt;
}

void dfs2(int u,int pr,int bos)
{
    dfn[u] = tim++;
    pre[u] = pr;
    ttp[u] = bos;
    int v;

    if(nxt[u] == u) return;
    dfs2(nxt[u],u,bos);

    for(int i = head[u]; i != -1; i = eg[i].next)
    {
        v = eg[i].v;
        if(v == pr || v == nxt[u]) continue;
        dfs2(v,u,v);
    }
}

void Change(int root,int l,int r,int pos,int x)
{
    if(l == r)
    {
        bit[0][root] = bit[1][root] = x;
        return;
    }

    int mid = (l+r)>>1;
    if(mid >= pos) Change(root<<1,l,mid,pos,x);
    else Change(root<<1|1,mid+1,r,pos,x);
    bit[0][root] = bit[0][root<<1]+bit[0][root<<1|1];
    bit[1][root] = max(bit[1][root<<1],bit[1][root<<1|1]);
}

int Sum(int root,int l,int r,int ll,int rr)
{
    //printf("%d %d %d\n",l,r,bit[0][root]);
    if(l == ll && r == rr) return bit[0][root];

    int mid = (l+r)>>1;
    if(mid >= rr) return Sum(root<<1,l,mid,ll,rr);
    else if(mid+1 <= ll) return Sum(root<<1|1,mid+1,r,ll,rr);
    else return Sum(root<<1,l,mid,ll,mid)+Sum(root<<1|1,mid+1,r,mid+1,rr);
}

int Max(int root,int l,int r,int ll,int rr)
{
    if(l == ll && r == rr) return bit[1][root];

    int mid = (l+r)>>1;
    if(mid >= rr) return Max(root<<1,l,mid,ll,rr);
    else if(mid+1 <= ll) return Max(root<<1|1,mid+1,r,ll,rr);
    else return max(Max(root<<1,l,mid,ll,mid),Max(root<<1|1,mid+1,r,mid+1,rr));
}

void solve(char ch,int a,int b)
{
    if(ch == 'H') Change(1,0,n-1,dfn[a],b);
    else if(ch == 'M')
    {
        int ans = -INF;
        while(ttp[b] != ttp[a])
        {
            if(dfn[b] < dfn[a]) swap(b,a);
            //printf("%d-%d %d-%d",ttp[b],b,dfn[ttp[b]],dfn[b]);
            ans = max(ans,Max(1,0,n-1,dfn[ttp[b]],dfn[b]));
            b = pre[ttp[b]];
        }
        if(dfn[b] < dfn[a]) swap(b,a);
        if(a) ans = max(ans,Max(1,0,n-1,dfn[a],dfn[b]));
        printf("%d\n",ans);
    }
    else
    {
        int ans = 0;
        while(ttp[b] != ttp[a])
        {
            if(dfn[b] < dfn[a]) swap(b,a);
            //printf("%d-%d %d-%d\n",ttp[b],b,dfn[ttp[b]],dfn[b]);
            ans += Sum(1,0,n-1,dfn[ttp[b]],dfn[b]);
            b = pre[ttp[b]];
        }
        if(dfn[b] < dfn[a]) swap(b,a);
        if(a) 
        {
            //printf("%d-%d %d-%d\n",a,b,dfn[a],dfn[b]);
            ans += Sum(1,0,n-1,dfn[a],dfn[b]);
        }
        printf("%d\n",ans);
    }
}


int main()
{
    //fread("in.in");
    //fwrite("run.out");

    int u,v,x;

    while(~scanf("%d",&n))
    {
        memset(head,-1,sizeof(head));
        tp = 0;

        for(int i = 1; i < n; ++i)
        {
            scanf("%d%d",&u,&v);
            Add(u,v);
            Add(v,u);
        }

        dfs1(1,1);
        ttp[0] = 1;
        tim = 0;
        dfs2(1,0,1);

        memset(bit,0,sizeof(bit));
        for(int i = 1; i <= n; ++i)
        {
            scanf("%d",&x);
            //printf("%d:fa:%d dfn:%d top:%d\n",i,pre[i],dfn[i],ttp[i]);
            Change(1,0,n-1,dfn[i],x);
        }

        int m;
        char opt[6];
        int a,b;
        scanf("%d",&m);
        while(m--)
        {
            scanf("%s%d%d",opt,&a,&b);
            solve(opt[1],a,b);
        }
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值