题意:

现在有 nnn 个城市,构成了一颗树。每个城市都有自己信仰的宗教,以及城市评级。现在一共有四种操作:
①①① 某个城市改信 ccc 教
②②② 某个城市的评级调整为 www
③③③ x−>yx->yx−>y 路径上所有与 xxx 信仰相同的城市的评级之和
④④④ x−>yx->yx−>y 路径上所有与 xxx 信仰相同的城市的评级最大值

(N,Q≤105,C≤105)(N,Q \leq 10^5,C \leq 10^5)(N,Q≤105,C≤105)


思路:

需要维护的操作只是单点修改和区间最值与最大值,维护的操作都不难。主要困难的地方在于最值和最大值都只在信仰相同的城市之间统计,因此我们需要对每个信仰都建一颗线段树。

但是由于空间的限制,对每个宗教都建一颗完整的线段树是不可能的,因此我们需要动态开点的操作。 采用动态开点的原因是本题初始最多只有 10510^5105 个点,操作最多也只有 10510^5105 次,因此有效的点最多只有 2∗1052*10^52∗105 个,所以我们只需要维护这些有效点即可。

我们再来仔细讲一下动态开点的原理。如下图所示,现在只有点 AAA 是有效点,因此我们只需给 root−>Aroot->Aroot−>A 路径上的点分配空间,不需要给其他的点分配空间,因此就达到了简化空间的目的。所以动态开一个点的空间费用最多是 lognlognlogn,我们只需要给没颗树记一个根节点,以及每个节点对应的左右儿子编号即可。因此我们也不需要之前建树的 buildbuildbuild 函数了,用 updateupdateupdate 函数动态插入每个点即可。

在 updateupdateupdate 函数中,传入的是一个引用,可以直接赋值,sz是当前线段树中总的节点个数。

void update(int& now, int l, int r, int x, int c){   //单点修改if(!now) now = ++sz;if(l == r){val[now] = c, maxn[now] = c;return;} int mid = (l+r)>>1;if(x <= mid) update(ls[now],l,mid,x,c);if(x > mid) update(rs[now],mid+1,r,x,c);val[now] = val[ls[now]]+val[rs[now]];maxn[now] = max(maxn[ls[now]],maxn[rs[now]]);
}

回到这道题来,解决的思路就很简单了。给每个宗教建一颗线段树,维护每颗线段树的根节点编号,然后对于每个线段树进行查询和修改即可,一个点从信仰 aaa 变为信仰 bbb,只需在 aaa 线段树中将这个点赋为 000,然后在 bbb 线段树将这个点再赋值即可。


总结:

总结一下动态开点线段树的常见使用方式。
大部分的线段树题目我们还是倾向于直接建树,因为更加方便易懂。但是对于一些有效节点个数不多,但是需要建多颗线段树的题目就需要考虑动态开点的技术,如主席树以及本题。


代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
typedef long long ll;
typedef double db;
const db EPS = 1e-9;
using namespace std;
const int N = 2*1e5+1000;int ls[20*N], rs[20*N], val[20*N], maxn[20*N], sz, rt[N];
int n,m,head[N],tot,son[N],size[N],d[N],id[N],rk[N],fa[N],top[N],ans1,ans2;
int re[N],lev[N];
struct Edge{int to,next;
}e[N];void init() { tot = 1, memset(head,0,sizeof head); }
void add(int x, int y) { e[++tot].to = y, e[tot].next = head[x], head[x] = tot; }
void dfs1(int x){   //求出每个点的子树大小、深度、重儿子size[x] = 1, d[x] = d[fa[x]]+1, son[x] = 0;for(int v,i = head[x]; i; i = e[i].next)if((v = e[i].to)!=fa[x]){fa[v] = x, dfs1(v), size[x] += size[v];if(size[son[x]] < size[v])son[x] = v;}
}
void dfs2(int x, int tp){   //求出每个节点的dfs序, dfs序对应的节点, 以及每个点所在链的顶端节点top[x] = tp, id[x] = ++tot, rk[tot] = x;if(son[x]) dfs2(son[x],tp);for(int v,i = head[x]; i; i = e[i].next)if((v = e[i].to)!=fa[x] && v!=son[x]) dfs2(v,v);
}
void update(int& now, int l, int r, int x, int c){  //单点修改if(!now) now = ++sz;if(l == r){val[now] = c, maxn[now] = c;return;} int mid = (l+r)>>1;if(x <= mid) update(ls[now],l,mid,x,c);if(x > mid) update(rs[now],mid+1,r,x,c);val[now] = val[ls[now]]+val[rs[now]];maxn[now] = max(maxn[ls[now]],maxn[rs[now]]);
}
void query(int now, int l, int r, int xx, int yy){if(l >= xx && r <= yy){ans1 += val[now], ans2 = max(ans2,maxn[now]);return;}int mid = (l+r)>>1;if(xx <= mid) query(ls[now],l,mid,xx,yy);if(yy > mid) query(rs[now],mid+1,r,xx,yy);
}
inline void updates(int x, int y){  //区间加z, 将区间分为多条链int tp = re[x];while(top[x] != top[y]){if(d[top[x]] < d[top[y]]) swap(x,y);query(rt[tp],1,n,id[top[x]],id[x]); //对于每条链直接修改x = fa[top[x]];}if(id[x] > id[y]) swap(x,y);query(rt[tp],1,n,id[x],id[y]);
}
int main()
{scanf("%d%d",&n,&m);rep(i,1,n) scanf("%d%d",&lev[i],&re[i]);init();rep(i,1,n-1){int xx,yy; scanf("%d%d",&xx,&yy);add(xx,yy), add(yy,xx);}tot = sz = 0, dfs1(1), dfs2(1,1);rep(i,1,n) update(rt[re[i]],1,n,id[i],lev[i]);rep(i,1,m){char s[10]; int x,y;scanf("%s",s);if(s[1] == 'C'){scanf("%d%d",&x,&y);update(rt[re[x]],1,n,id[x],0);update(rt[y],1,n,id[x],lev[x]);re[x] = y;}else if(s[1] == 'W'){scanf("%d%d",&x,&y);update(rt[re[x]],1,n,id[x],y);lev[x] = y;}else{scanf("%d%d",&x,&y);ans1 = ans2 = 0;updates(x,y);if(s[1] == 'S') printf("%d\n",ans1);else printf("%d\n",ans2);}}return 0;
}

【BZOJ 3531 Sdoi2014】旅行【动态开点线段树+树链剖分】相关推荐

  1. BZOJ 3531[Sdoi2014]旅行

    BZOJ 3531[Sdoi2014]旅行 题面描述 传送门 题目分析 可以考虑到,如果这个题所有城市都只信一种宗教的话,就是一个sb树剖,直接进行链的查询和修改就能搞定.多个宗教的话,可以有一种暴力 ...

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

    题目链接:BZOJ - 3531 题目分析 题目询问一条路径上的信息时,每次询问有某种特定的文化的点. 每个点的文化就相当于一种颜色,每次询问一条路径上某种颜色的点的信息. 可以使用离线算法, 类似于 ...

  3. 动态开点线段树(多棵线段树)的内存分配与回收

    前言 线段树,是一个很好用的能支持O(logn)区间操作的数据结构,随着做一些稍微烦一点的题,有时候会发现有些情况要开一个数组的线段树,更有甚者要树套树,而在很多情况下线段树就不能把所有点都开满了(否 ...

  4. 【线段树合并】解题报告:luogu P4556雨天的尾巴 (树上对点差分 + 动态开点 + 线段树合并)线段树合并模板离线/在线详解

    题目链接:雨天的尾巴 本题本身是一个非常简单的一道树上差分的模板题,但是由于变态的数据范围,我们直接用数组是存不下的(本来使用一颗普通的线段树直接维护最大值即可.但是本题的空间只有128MB,直接按照 ...

  5. 洛谷P3960 列队(动态开节点线段树)

    题意 题目链接 Sol 看不懂splay..,看不懂树状数组... 只会暴力动态开节点线段树 观察之后不难发现,我们对于行和列需要支持的操作都是相同的:找到第\(k\)大的元素并删除,在末尾插入一个元 ...

  6. CF1045G AI robots(动态开点线段树)

    题意 火星上有$N$个机器人排成一行,第$i$个机器人的位置为$x_{i}$,视野为$r_{i}$,智商为$q_{i}$.我们认为第$i$个机器人可以看到的位置是$[x_{i}-r_{i},x_{i} ...

  7. NOIP2017 列队——动态开点线段树

    Description: Sylvia 是一个热爱学习的女♂孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有n×m名学生,方阵的行数为  ...

  8. codeforces 915E - Physical Education Lessons 动态开点线段树

    题意: 最大$10^9$的区间, $3*10^5$次区间修改,每次操作后求整个区间的和 题解: 裸的动态开点线段树,计算清楚数据范围是关键... 经过尝试 $2*10^7$会$MLE$ $10^7$会 ...

  9. HDU 6681 Rikka with Cake(扫描线、动态开点线段树)

    http://acm.hdu.edu.cn/showproblem.php?pid=6681 题意 在矩形区域内有k条射线,问这些射线将矩形分成了多少区域 题解 容易发现答案为所有射线交点个数+1. ...

  10. 洛谷P3120 [USACO15FEB]牛跳房子(动态开节点线段树)

    题意 题目链接 Sol \(f[i][j]\)表示前\(i\)行\(j\)列的贡献,转移的时候枚举从哪里转移而来,复杂度\(O(n^4)\) 然后考虑每一行的贡献,动态开节点线段树维护一下每种颜色的答 ...

最新文章

  1. 关于介绍编程前景的html文档,HTML编程基础稿件(32页)-原创力文档
  2. mysql隐藏密码_MySQL在Linux系统中隐藏命令行中的密码的方法
  3. python pillow 扩展图片增强
  4. NIO中的SelectionKey
  5. 递归函数反向显示单链表
  6. 【自己给自己题目做】之一:椭圆可点击区域
  7. (第九周)团队项目14
  8. 前端学习(2217):react元素渲染之时钟
  9. 编译原理:正规式转变成DFA算法
  10. 谷粒商城:17.商城业务 — Nginx搭建域名访问
  11. 频繁分配释放内存导致的性能问题的分析
  12. c语言if大括号的作用,c语言if语句 用法是什么
  13. python取出列表的第一列_python取第一列
  14. javascript Date format(js日期格式化)
  15. FFmpeg视频解码流程详解及demo
  16. 云计算、公有云、私有云、混合云等
  17. 关于使用Artery插件的一些积累1
  18. 云计算技术 — 混合云 — Overview
  19. 接手别人的代码,死的心有吗?
  20. 洛谷 P1594 护卫队

热门文章

  1. 程序员的恶性循环:加班-没空学习-老是写同等水平代码-无法提升代码质量-老是出BUG-老是需要修改-加班-......
  2. android ViewFlipper的使用
  3. 基于OpenCV库的Gabor滤波器的实现
  4. Oracle PL/SQL中使用%TYPE和%ROWTYPE的方法
  5. java 调用存储过程structdescriptor_Spring SimpleJdbcCall如何在存储过程调用中为oracle STRUCT指定模式...
  6. python中的作用域_python中作用域
  7. java list stream 去除 null_Stream流的这些操作,你得知道,对你工作有很大帮助
  8. typedef 与结构体struct
  9. oracle创建默认序列号,PLS-00103:为序列号oracle创建触发器(PLS-00103: Create trigger for sequence number oracle)...
  10. POJ 3461题解(kmp算法)文本串和子串匹配