BZOJ 3531 SDOI 2014 旅行

题目大意

给出一个树,树上每个节点有两个权值,分别是这个节点的宗教评级和这个节点信仰的宗教。多次修改这两个权值,每次询问树上路径上的点的同一个宗教的最大评级和评级和。

思路

不要想太多,每个宗教建立一颗线段树,空间开不下考虑一下动态节点线段树。之后在每个线段树上维护一下树链剖分就行了。

你们想知道c的取值范围么?

[0,10^5]

CODE

#define _CRT_SECURE_NO_WARNINGS

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

struct SegTree *nil;

struct SegTree{
    SegTree *son[2];
    int _max,sum;

    SegTree() {
        _max = sum = 0;
        son[0] = son[1] = nil;
    }

    void Modify(int l,int r,int x,int c) {
        if(l == r) {
            _max = sum = c;
            return ;
        }
        int mid = (l + r) >> 1;
        if(x <= mid) {
            if(son[0] == nil)
                son[0] = new SegTree();
            son[0]->Modify(l,mid,x,c);
        }
        else {
            if(son[1] == nil)
                son[1] = new SegTree();
            son[1]->Modify(mid + 1,r,x,c);
        }
        _max = max(son[0]->_max,son[1]->_max);
        sum = son[0]->sum + son[1]->sum;
    }
    int GetSum(int l,int r,int x,int y) {
        if(this == nil) return 0;
        if(l == x && y == r)    return sum;
        int mid = (l + r) >> 1;
        if(y <= mid) return son[0]->GetSum(l,mid,x,y);
        if(x > mid)      return son[1]->GetSum(mid + 1,r,x,y);
        int left = son[0]->GetSum(l,mid,x,mid);
        int right = son[1]->GetSum(mid + 1,r,mid + 1,y);
        return left + right;
    }
    int GetMax(int l,int r,int x,int y) {
        if(this == nil) return 0;
        if(l == x && y == r)    return _max;
        int mid = (l + r) >> 1;
        if(y <= mid) return son[0]->GetMax(l,mid,x,y);
        if(x > mid)      return son[1]->GetMax(mid + 1,r,x,y);
        int left = son[0]->GetMax(l,mid,x,mid);
        int right = son[1]->GetMax(mid + 1,r,mid + 1,y);
        return max(left,right);
    }
}none,*root[MAX];

int points,asks;
int head[MAX],total;
int _next[MAX << 1],aim[MAX << 1];

inline void Add(int x,int y)
{
    _next[++total] = head[x];
    aim[total] = y;
    head[x] = total;
}

int p[MAX],belief[MAX];

int size[MAX],son[MAX],father[MAX],deep[MAX];
int top[MAX],pos[MAX],cnt;

void PreDFS(int x,int last)
{
    deep[x] = deep[last] + 1;
    father[x] = last;
    size[x] = 1;
    int max_size = 0;
    for(int i = head[x]; i; i = _next[i]) {
        if(aim[i] == last)  continue;
        PreDFS(aim[i],x);
        size[x] += size[aim[i]];
        if(size[aim[i]] > max_size)
            max_size = size[aim[i]],son[x] = aim[i];
    }
}

void DFS(int x,int last,int _top)
{
    pos[x] = ++cnt;
    top[x] = _top;
    if(son[x])  DFS(son[x],x,_top);
    for(int i = head[x]; i; i = _next[i]) {
        if(aim[i] == last || aim[i] == son[x])  continue;
        DFS(aim[i],x,aim[i]);
    }
}

inline int GetSum(int x,int y,int p)
{
    int re = 0;
    while(top[x] != top[y]) {
        if(deep[top[x]] < deep[top[y]])  swap(x,y);
        re += root[p]->GetSum(1,points,pos[top[x]],pos[x]);
        x = father[top[x]];
    }
    if(deep[x] < deep[y])    swap(x,y);
    re += root[p]->GetSum(1,points,pos[y],pos[x]);
    return re;
}

inline int GetMax(int x,int y,int p)
{
    int re = 0;
    while(top[x] != top[y]) {
        if(deep[top[x]] < deep[top[y]])  swap(x,y);
        re = max(re,root[p]->GetMax(1,points,pos[top[x]],pos[x]));
        x = father[top[x]];
    }
    if(deep[x] < deep[y])    swap(x,y);
    re = max(re,root[p]->GetMax(1,points,pos[y],pos[x]));
    return re;
}

char s[10];

int main()
{

    nil = &none;

    cin >> points >> asks;
    for(int i = 0; i < MAX; ++i)
        root[i] = new SegTree();
    for(int i = 1; i <= points; ++i)
        scanf("%d%d",&p[i],&belief[i]);
    for(int x,y,i = 1; i < points; ++i) {
        scanf("%d%d",&x,&y);
        Add(x,y),Add(y,x);
    }
    PreDFS(1,0);
    DFS(1,0,1);
    for(int i = 1; i <= points; ++i)
        root[belief[i]]->Modify(1,points,pos[i],p[i]);
    for(int x,y,i = 1; i <= asks; ++i) {
        scanf("%s%d%d",s,&x,&y);
        if(s[0] == ‘C‘ && s[1] == ‘C‘) {
            root[belief[x]]->Modify(1,points,pos[x],0);
            root[belief[x] = y]->Modify(1,points,pos[x],p[x]);
        }
        else if(s[0] == ‘C‘ && s[1] == ‘W‘)
            root[belief[x]]->Modify(1,points,pos[x],p[x] = y);
        else if(s[0] == ‘Q‘ && s[1] == ‘S‘)
            printf("%d\n",GetSum(x,y,belief[x]));
        else
            printf("%d\n",GetMax(x,y,belief[x]));
    }
    return 0;
}
时间: 2024-08-11 09:51:55

BZOJ 3531 SDOI 2014 旅行的相关文章

【BZOJ 3531】 [Sdoi2014]旅行

3531: [Sdoi2014]旅行 Time Limit: 20 Sec Memory Limit: 512 MB Submit: 575 Solved: 303 [Submit][Status][Discuss] Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足 从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教,如飞天面条神教.隐形独角兽教.绝地教都是常见的信仰.为了方便,我们用不同的正整数代表各种宗教, S国的居民常常旅行.旅行时他们总会走

BZOJ 3533 sdoi 2014 向量集

设(x,y)为Q的查询点,分类讨论如下:1.y>0:  最大化a*x+b*y,维护一个上凸壳三分即可 2.y<0:最大化a*x+b*y  维护一个下凸壳三分即可 我们考虑对时间建出一棵线段树 对于每个区间,如果满了就做出两个凸壳 总时间复杂度是O(n*log^2n) 之后我们考虑查询,每个区间最多被分解为log(n)个区间 在每个区间的凸壳上三分最优解即可 至于优化,可以设定一个阈值,当区间长度小于阈值时不用做凸壳,查询时直接暴力就可以了 #include<cstdio> #inc

[BZOJ 3531] [Sdoi2014] 旅行 【离线+LCT】

题目链接:BZOJ - 3531 题目分析 题目询问一条路径上的信息时,每次询问有某种特定的文化的点. 每个点的文化就相当于一种颜色,每次询问一条路径上某种颜色的点的信息. 可以使用离线算法, 类似于“郁闷的小 J ” 那道题目.将各种操作和询问按照颜色为第一关键字,时间为第二关键字排序. 那么修改颜色的操作就相当于在原颜色中是删点,在新颜色中是加点. 处理完一种颜色的操作后,要将这个颜色的点都做一次删除操作,这样,对于处理下一种颜色,树就又是空的了. 这种题,思考的时候有点晕,写代码的时候非常

BZOJ 3531(树链剖分+线段树)

Problem 旅行 (BZOJ 3531) 题目大意 给定一颗树,树上的每个点有两个权值(x,y). 要求维护4种操作: 操作1:更改某个点的权值x. 操作2:更改某个点的权值y. 操作3:求a-->b路径上所有x属性与a,b相同的点y属性的和. 操作4:求a-->b路径上所有x属性与a,b相同的点y属性的最大值. N,Q ,x <= 10^5  ,  y <= 10^4 解题分析 由于x属性的范围较大,无法直接统计. 考虑每次修改为单点修改,询问时只对相同x属性的询问. 因此,

[BZOJ 1879][SDOI 2009]Bill的挑战 题解(状压DP)

[BZOJ 1879][SDOI 2009]Bill的挑战 Description Solution 1.考虑状压的方式. 方案1:如果我们把每一个字符串压起来,用一个布尔数组表示与每一个字母的匹配关系,那么空间为26^50,爆内存: 方案2:把每一个串压起来,多开一维记录匹配字符,那么空间为nlen26,合法,但不便于状态的设计和转移: 方案3:把每一个串同一个位置的字符放在一起,用一个布尔数组记录与每一个小写字母的匹配关系,那么空间为26^15*len,爆内存: 方案4:把每一个串同一个位置

【BZOJ 3531】【SDOI 2014】旅行 树链剖分

因为有$10^5$个宗教,需要开$10^5$个线段树. 平时开的线段树是“满”二叉树,但在这个题中代表一个宗教的线段树管辖的区间有很多点都不属于这个宗教,也就不用“把枝叶伸到这个点上”,所以这样用类似主席树的数组动态开点来建立$10^5$个只有几个“树枝”的线段树,维护轻重链就可以了 线段树的$L,R,l,r$弄反了调了好久$QAQ$ $so$ $sad$ #include<cstdio> #include<cstring> #include<algorithm> #d

bzoj 3531 [Sdoi2014]旅行(树链剖分,线段树)

3531: [Sdoi2014]旅行 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 876  Solved: 446[Submit][Status][Discuss] Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足 从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教,如飞天面条神教.隐形独角兽教.绝地教都是常见的信仰.为了方便,我们用不同的正整数代表 各种宗教,  S国的居民常常旅行.旅行时他们总

[BZOJ 3531][Sdoi2014]旅行(树链剖分+线段树)

Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教,如飞天面条神教.隐形独角兽教.绝地教都是常见的信仰.为了方便,我们用不同的正整数代表各种宗教,  S国的居民常常旅行.旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿.当然旅程的终点也是信仰与他相同的城市.S国政府为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值. 在S国的历史

BZOJ.3531.旅行(树链剖分 动态开点)

题目链接 无优化版本(170行): /* 首先树剖可以维护树上的链Sum.Max 可以对每个宗教建一棵线段树,那这题就很好做了 不过10^5需要动态开点 (不明白为什么nlogn不需要回收就可以 不是每个Insert加log个节点?) 操作修改完更改原数列!盲人..少玩rts.. */ #include<cstdio> #include<cctype> #include<algorithm> #define gc() getchar() #define now node