BZOJ-3531 luogu-P3313 树链剖分 + 线段树 + 动态开点

题目链接 luogu - P3313

题意:中文题

思路:用树链剖分处理图后,可对每一个宗教用一颗线段树维护其区间内的和以及最大值,由于内存有限,故使用动态开点从而节省空间(本来花了大概一小时就完成了,结果把区间查询写成了单点查询,花了一个小时才改出来。。。)

#include <iostream>
#include <stdio.h>

using namespace std;

const int N = 1e5 + 10, MAXN = 3e6;

struct edge
{
    int to, next;
}Edge[N<<1];

int cnt, head[N], bel[N], qua[N]; //bel[]储存每个城市的信仰,qua[]储存每个城市的评级
int f[N], son[N], num[N], dep[N], id[N], top[N];
int R[N], lc[MAXN], rc[MAXN], sum[MAXN], MAX[MAXN];

inline int max(int a,int b){ return a > b ? a : b; }

inline int read()
{
    char c;
    int ans = 0;
    c = getchar();
    while(!('0' <= c && c <= '9')) c = getchar();
    while('0' <= c && c <= '9'){
        ans = ans * 10 + c - '0';
        c = getchar();
    }
    return ans;
}

inline void add_edge(int from,int to)
{
    Edge[++cnt].to = to;
    Edge[cnt].next = head[from];
    head[from] = cnt;
}

void dfs1(int v)
{
    num[v] = 1;
    dep[v] = dep[f[v]] + 1;
    for(int i = head[v]; i; i = Edge[i].next){
        int to = Edge[i].to;
        if(to == f[v]) continue;
        f[to] = v;
        dfs1(to);
        num[v] += num[to];
        if(num[to] > num[son[v]])
            son[v] = to;
    }
}

void dfs2(int v,int tp)
{
    top[v] = tp;
    id[v] = ++cnt;
    if(!son[v]) return ;
    dfs2(son[v],tp);
    for(int i = head[v]; i; i = Edge[i].next){
        int to = Edge[i].to;
        if(to == son[v] || to == f[v]) continue;
        dfs2(to,to);
    }
}

void update(int L,int C,int l,int r,int &rt)
{
    if(!rt) rt = ++cnt;
    if(l == r){
        sum[rt] = MAX[rt] = C;
        return ;
    }
    int m = (l+r)>>1;
    if(L <= m) update(L,C,l,m,lc[rt]);
    else	   update(L,C,m+1,r,rc[rt]);
    sum[rt] = sum[lc[rt]] + sum[rc[rt]];
    MAX[rt] = max(MAX[lc[rt]], MAX[rc[rt]]);
}

void erase(int L,int l,int r,int &rt)
{
    if(l == r){
        sum[rt] = MAX[rt] = 0;
        return ;
    }
    int m = (l+r)>>1;
    if(L <= m) erase(L,l,m,lc[rt]);
    else	   erase(L,m+1,r,rc[rt]);
    sum[rt] = sum[lc[rt]] + sum[rc[rt]];
    MAX[rt] = max(MAX[lc[rt]], MAX[rc[rt]]);
}

int queryMAX(int L,int R,int l,int r,int rt)
{
    if(!rt) return 0;
    if(L <= l && r <= R)
        return MAX[rt];
    int m = (l+r)>>1, ans = 0;
    if(L <= m) ans = max(ans, queryMAX(L,R,l,m,lc[rt]));
    if(m <  R) ans = max(ans, queryMAX(L,R,m+1,r,rc[rt]));
    return ans;
}

int querySUM(int L,int R,int l,int r,int rt)
{
    if(!rt) return 0;
    if(L <= l && r <= R)
        return sum[rt];
    int m = (l+r)>>1, ans = 0;
    if(L <= m) ans += querySUM(L,R,l,m,lc[rt]);
    if(m <  R) ans += querySUM(L,R,m+1,r,rc[rt]);
    return ans;
}

void queryMAXs(int A,int B)
{
    int ans = 0, r = bel[A];
    while(top[A] != top[B]){
        if(dep[top[A]] < dep[top[B]]) swap(A,B);
        ans = max(ans, queryMAX(id[top[A]],id[A],1,1e5,R[r]));
        A = f[top[A]];
    }
    if(id[A] > id[B]) swap(A,B);
    printf("%d\n", max(ans, queryMAX(id[A],id[B],1,1e5,R[r])));
}

void querySUMs(int A,int B)
{
    int ans = 0, r = bel[A];
    while(top[A] != top[B]){
        if(dep[top[A]] < dep[top[B]]) swap(A,B);
        ans += querySUM(id[top[A]],id[A],1,1e5,R[r]);
        A = f[top[A]];
    }
    if(id[A] > id[B]) swap(A,B);
    printf("%d\n", ans + querySUM(id[A],id[B],1,1e5,R[r]));
}

int main()
{
    char oper[2];
    int n, m, A, B;
    n = read(); m = read();
    for(int i = 1; i <= n; ++i){
        A = read(); B = read();
        bel[i] = B, qua[i] = A;
    }
    for(int i = 0; i < n - 1; ++i){
        A = read(); B = read();
        add_edge(A,B);
        add_edge(B,A);
    }
    cnt = 0; dfs1(1); dfs2(1,1); cnt = 0;
    for(int i = 1; i <= n; ++i){
        update(id[i],qua[i],1,1e5,R[bel[i]]);
    }
    while(m--){
        scanf("%s",oper); A = read(); B = read();
        if(oper[1] == 'S')
            querySUMs(A,B);
        else if(oper[1] == 'M')
            queryMAXs(A,B);
        else if(oper[1] == 'C'){
            erase(id[A],1,1e5,R[bel[A]]); //将原来宗教在该城市的值去除
            bel[A] = B;
            update(id[A],qua[A],1,1e5,R[B]);
        }
        else{
            update(id[A],B,1,1e5,R[bel[A]]);
            qua[A] = B;
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值