【codevs3306】水果姐逛水果街Ⅲ 树链剖分

题目描述 Description

连续两天都没有被cgh难倒,水果姐心情很不错,第三天又来逛水果街。

今天cgh早早就来到了水果街等水果姐,因为他带来了帮手cys。cgh的魔法是把水果街变成树结构,而cys的魔法是瞬间改变某家店的水果价格。一边问一边改,绝不给水果姐思考的时间!

同样还是n家水果店,编号为1~n,每家店能买水果也能卖水果,并且同一家店卖与买的价格一样。

cgh和cys一共有m个操作。

操作的格式为

0 x y 获 1 x y

如果操作类型是0,表示cys把第x家店的价格改为y

如果操作类型是1,则表示要求水果姐从第x家店出发到第y家店,途中只能选一家店买一个水果,然后选一家店(可以是同一家店,但不能往回走)卖出去。并且输出最多可以赚多少钱。

这题是给天牛做的。

输入描述 Input Description

第一行n,表示有n家店

下来n个正整数,表示每家店一个苹果的价格。

下来n-1行,每行两个整数x,y,表示第x家店和第y家店有一条边。

下来一个整数m,表示下来有m个操作。

下来有m行,每行三个整数,表示一次操作。

输出描述 Output Description

每行对应一个1类型的操作,输出一个整数,表示面对cgh的每次询问,水果姐最多可以赚到多少钱。

样例输入 Sample Input

4
16 5 1 15
1 2
1 3
2 4
3
1 3 4
0 3 17
1 4 3

样例输出 Sample Output

15
12

数据范围及提示 Data Size & Hint

0<=苹果的价格<=10^8

0 < n<=2*10^5

0 < m<=10^5


思路和前两个:水果姐逛水果街Ⅰ水果姐逛水果街Ⅱ思路差不多。

重点是查询答案的时候不要写反顺序!!!

挺有意思的树剖裸题。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int SZ = 1000010;
const int INF = 1000000010;

int head[SZ],nxt[SZ],to[SZ],tot = 0;

int num[SZ];

void build_edge(int f,int t)
{
    to[++ tot] = t;
    nxt[tot] = head[f];
    head[f] = tot;
}

int sz[SZ],fa[SZ],son[SZ],top[SZ],inseg[SZ],intree[SZ],deep[SZ];

void dfs_1(int u,int f)
{
    sz[u] = 1;
    fa[u] = f;
    deep[u] = deep[f] + 1;
    for(int i = head[u];i;i = nxt[i])
    {
        int v = to[i];
        if(v == f) continue;
        dfs_1(v,u);
        sz[u] += sz[v]; 
        if(!son[u] || sz[son[u]] < sz[v]) son[u] = v;
    }
}

int totp = 0;

void dfs_2(int u,int tp)
{
    top[u] = tp;
    inseg[u] = ++ totp;
    intree[totp] = u;
    if(!son[u]) return ;
    dfs_2(son[u],tp);
    for(int i = head[u];i;i = nxt[i])
    {
        int v = to[i];
        if(v == fa[u] || v == son[u]) continue;
        dfs_2(v,v);
    }
}


struct segment{
    int l,r;
    int lans,rans,maxn,minn;
}tree[SZ << 2];

void update(int p)
{
    int lch = p << 1,rch = p << 1 | 1; 
    tree[p].maxn = max(tree[lch].maxn,tree[rch].maxn); 
    tree[p].minn = min(tree[lch].minn,tree[rch].minn);
    tree[p].lans = max(max(tree[lch].lans,tree[rch].lans),tree[rch].maxn - tree[lch].minn); 
    tree[p].rans = max(max(tree[lch].rans,tree[rch].rans),tree[lch].maxn - tree[rch].minn); 
}

void build_seg(int p,int l,int r)
{
    tree[p].l = l;  tree[p].r = r;
    if(l == r) 
    {
        tree[p].maxn = tree[p].minn = num[intree[l]];
        return ;
    }
    int mid = (l + r) >> 1;
    build_seg(p << 1,l,mid);
    build_seg(p << 1 | 1,mid + 1,r);
    update(p);
}

int ask_min(int p,int l,int r)
{
    if(l <= tree[p].l && tree[p].r <= r) return tree[p].minn;
    int mid = (tree[p].l + tree[p].r) >> 1;
    int ans = INF;
    if(l <= mid) ans = min(ans,ask_min(p << 1,l,r));
    if(mid < r) ans = min(ans,ask_min(p << 1 | 1,l,r));
    return ans;
}

int ask_max(int p,int l,int r)
{
    if(l <= tree[p].l && tree[p].r <= r) return tree[p].maxn;
    int mid = (tree[p].l + tree[p].r) >> 1;
    int ans = -INF;
    if(l <= mid) ans = max(ans,ask_max(p << 1,l,r));
    if(mid < r) ans = max(ans,ask_max(p << 1 | 1,l,r));
    return ans;
}

int ask_lans(int p,int l,int r)
{
    if(l <= tree[p].l && tree[p].r <= r) return tree[p].lans;
    int mid = (tree[p].l + tree[p].r) >> 1;
    int ans = -INF;
    if(l <= mid && mid < r) ans = max(ans,ask_max(1,mid + 1,r) - ask_min(1,l,mid));
    if(l <= mid) ans = max(ans,ask_lans(p << 1,l,r));
    if(mid < r) ans = max(ans,ask_lans(p << 1 | 1,l,r));
    return ans;
}   

int ask_rans(int p,int l,int r)
{
    if(l <= tree[p].l && tree[p].r <= r) return tree[p].rans;
    int mid = (tree[p].l + tree[p].r) >> 1;
    int ans = -INF;
    if(l <= mid && mid < r) ans = max(ans,ask_max(1,l,mid) - ask_min(1,mid + 1,r));
    if(l <= mid) ans = max(ans,ask_rans(p << 1,l,r));
    if(mid < r) ans = max(ans,ask_rans(p << 1 | 1,l,r));
    return ans;
}   

void change(int p,int pos,int d)
{
    if(tree[p].l == tree[p].r) { tree[p].maxn = tree[p].minn = d; return ; }
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(pos <= mid) change(p << 1,pos,d);
    else change(p << 1 | 1,pos,d);
    update(p);
}

int find_ans(int x,int y)
{
    if(inseg[x] < inseg[y]) return ask_lans(1,inseg[x],inseg[y]);
    else return ask_rans(1,inseg[y],inseg[x]);
}

int find_max(int x,int y)
{
    if(inseg[x] < inseg[y]) return ask_max(1,inseg[x],inseg[y]);
    else return ask_max(1,inseg[y],inseg[x]);
}

int find_min(int x,int y)
{
    if(inseg[x] < inseg[y]) return ask_min(1,inseg[x],inseg[y]);
    else return ask_min(1,inseg[y],inseg[x]);
}

int ask(int x,int y)
{
    int fx = top[x],fy = top[y];
    int ans = 0,maxn = -INF,minn = INF;
    while(fx != fy)
    {
        if(deep[fx] > deep[fy])
        {
            ans = max(ans,find_ans(x,fx));
            minn = min(minn,find_min(x,fx));
            ans = max(ans,maxn - minn);
            x = fa[fx]; fx = top[x];
        }
        else
        {
            ans = max(ans,find_ans(fy,y));
            maxn = max(maxn,find_max(fy,y));
            ans = max(ans,maxn - minn);
            y = fa[fy]; fy = top[y];
        }
    //  cout<<fx<<" "<<x<<" "<<fy<<" "<<y<<" "<<maxn<<" "<<minn<<" "<<ans<<endl; 
    }
    ans = max(ans,find_ans(x,y));
    ans = max(ans,max(find_max(x,y) - minn,maxn - find_min(x,y)));
//  printf("%d %d\n",maxn,minn);
    return ans;
}

int main()
{
//  freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);  
    int n;
    scanf("%d",&n);
    for(int i = 1;i <= n;i ++)
    {
        scanf("%d",&num[i]);
    }
    for(int i = 1;i <= n - 1;i ++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        build_edge(a,b); build_edge(b,a);
    }
    dfs_1(1,0); dfs_2(1,1);

    build_seg(1,1,n);



    int m;
    scanf("%d",&m);
    while(m --)
    {
        int opt,x,y;
        scanf("%d%d%d",&opt,&x,&y);
        if(opt) printf("%d\n",ask(x,y));
        else change(1,inseg[x],y);

/*  for(int i = 1;i <= n;i ++) 
        printf("%d ",ask_max(1,inseg[i],inseg[i])inseg[i]); 
    puts("");*/
    }
    return 0;
}


/*

11
92 84 39 31 19 18 96 10 54 76 98 
2 1
3 1
4 1
5 1
6 3
7 1
8 3
9 1
10 2
11 5
1
1 4 11


*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值