震惊,蒟蒻学树剖第二天就打题解

所以说,理解之后树剖这种东西其实难度真心不大.至少这种模板题都可以秒切的

这里推荐一个博客: 树剖详解

蒟蒻就是在这个博客上学到的

如果想看我自己写的总结,请点 我的博客

这个链接(虽然这个是写给自己看的,理解难度应该不小)

树剖的方法在博客上都有了,在这里不细讲,专注讲一下这题的实现:

\(dfs\) 请使用博客上的方法,这题需要做的只是照搬

首先,我们可以发现,这题跟普通的树剖基本上一样.唯一的区别就是他要使用 \(xor\) .那么,我们发现, \(xor\) 的操作其实跟加减没有任何区别.于是,我们只需要将加减法换成 \(xor\), 这题的操作就实现了

1.update

update跟正常的线段树update没有区别,而且他不需要lazy,因为他只需要update一个点.所以我们仍然是二分查找,找到就二分,然后他的父亲就更新为左儿子 \(xor\) 右儿子.

我们还可以观察到,如果更新右儿子,那么左儿子就不用更新了.因此,这个更新速度是恒定的 \(O(logn)\)

void update(int way, int l, int r, int q, int val){
if (q<l || q>r) return;//不在范围内(其实用处不大)
if (l==r && r==q) {seg[way]=val;return;}//刚好是这个数就更新
if (l==r) return;//否则不更新
int mid = (l+r)/2;
if (q<=mid)update(way*2,l,mid,q,val);//在左儿子的区间
if (q>mid)update(way*2+1,mid+1,r,q,val);//在右儿子的区间
seg[way] =seg[way*2]^seg[way*2+1];//用儿子更新父亲
}

2.query

这题的难点来了:怎么拿路径

我们可以发现,取两点之间的路径相当于分别取两点到他俩 \(lca\) 的值.

再观察,我们发现,在取 \(lca\) 途中答案可以直接更新,因为去lca的路径只有一条,所以,我们每次query的时候先将答案设成0,每次网上跳区间的时候答案就是 \(ans^区间\)

int query_up(int way, int l, int r, int qlow, int qhigh){
if (qlow<= l && r<=qhigh) return seg[way];//完全包围
if (l>qhigh || r<qlow) return 0;//不在范围 int mid = (l+r)/2;
return (query_up(way*2,l,mid,qlow,qhigh) ^ query_up(way*2+1,mid+1,r,qlow,qhigh));//左儿子^右儿子
}
int query(int x, int y){
int ans = 0;//设答案为0
while(top[x]!=top[y]){//如果不在同一条链
if (dep[top[x]]<dep[top[y]]) swap(x,y);//谁高谁低
ans ^= query_up(1,1,n,id[top[x]],id[x]);//更新答案
x = fat[top[x]];//跳到上面那条链
}
if (dep[x]>dep[y]) swap(x,y);
ans ^= query_up(1,1,n,id[x],id[y]);//找本链的值
return ans;
}

完整代码:

#include <iostream>
#include <algorithm>
#include <vector>
#include <fstream>
using namespace std;
const int MAXN = 1e5+5;
int res = 0;
int n,m,r,p,dep[MAXN],fat[MAXN],son[MAXN],sz[MAXN],num[MAXN],id[MAXN],top[MAXN],wt[MAXN],cnt=0,head[MAXN],tot = 0;
int seg[MAXN*4],lazy[MAXN*4];
vector<int> adj[MAXN];
void dfs1(int pos, int f, int depth){
dep[pos] = depth;
fat[pos] = f;
sz[pos] = 1;
int maxi = -1;
for (int v : adj[pos]){
if (v == f) continue;
dfs1(v,pos,depth+1);
sz[pos]+=sz[v];
if (maxi<sz[v]) {maxi = sz[v];son[pos] = v;}
}
}
bool vis[MAXN];
void dfs2(int pos, int top_pos){
id[pos] = ++cnt;
wt[cnt] = num[pos];
top[pos] = top_pos;
if (!son[pos]) return;
dfs2(son[pos],top_pos);
for (int v : adj[pos]){
if (v==fat[pos] || v==son[pos]) continue;
dfs2(v,v);
}
}//树剖的基础dfs
void make_tree(int way, int l, int r){
if (l==r) {seg[way] = wt[l];return;}
int mid = (l+r)/2;
make_tree(way*2,l,mid);
make_tree(way*2+1,mid+1,r);
seg[way] = seg[way*2] ^seg[way*2+1] ;
}//建树
void update(int way, int l, int r, int q, int val){
if (q<l || q>r) return;
if (l==r && r==q) {seg[way]=val;return;}
if (l==r) return;
int mid = (l+r)/2;
if (q<=mid)update(way*2,l,mid,q,val);
if (q>mid)update(way*2+1,mid+1,r,q,val);
seg[way] =seg[way*2]^seg[way*2+1];
}//更新
int query_up(int way, int l, int r, int qlow, int qhigh){
if (qlow<= l && r<=qhigh) return seg[way];
if (l>qhigh || r<qlow) return 0; int mid = (l+r)/2;
return (query_up(way*2,l,mid,qlow,qhigh) ^ query_up(way*2+1,mid+1,r,qlow,qhigh));
}//求区间
int query(int x, int y){
int ans = 0;
while(top[x]!=top[y]){
if (dep[top[x]]<dep[top[y]]) swap(x,y);
ans ^= query_up(1,1,n,id[top[x]],id[x]);
x = fat[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
ans ^= query_up(1,1,n,id[x],id[y]);
return ans;
}//求链
int main(){
cin >> n >> m;
for (int i=1;i<=n;i++) cin >> num[i];
for (int i=0;i<n-1;i++){
int a,b; cin >> a >> b;
adj[a].push_back(b);
adj[b].push_back(a);
}
dfs1(1,0,1);
dfs2(1,1);
make_tree(1,1,n);
//初始化
for (int i=0;i<m;i++){
int ind,a,b; cin >> ind >> a >> b;
if (ind==1){
update(1,1,n,id[a],b);
}else{
cout << query(a,b) << endl;
}//操作
}
}

复杂度 \(O(nlog^2n)\) 可以过

做完建议去做P3384,本蒟蒻就是做了那题才恍然大悟的

最新文章

  1. PowerDesigner 15.2入门学习 二
  2. 如何写好CSS?(OOCSS\DRY\SMACSS)
  3. Android_打开多个Activity,返回到第一个Activity
  4. Lucene40SkipListWriter
  5. 我的第一个spring_boot项目
  6. 用Flow编写更好的js代码
  7. JS 获取图片标签和所有的图片中的src的正则表达式
  8. sqlserver触发器insert,delete,update
  9. JVMj机制
  10. python常用模块之时间模块
  11. Spark学习笔记——文本处理技术
  12. &lt;Scala&gt;&lt;For beginners&gt;
  13. Json3:使用gson做节点解析
  14. Velvet1.2.10的安装和使用
  15. 消息中间件(一)MQ详解及四大MQ比较
  16. ElementUI select
  17. ⑥ 设计模式的艺术-06.建造者(Builder)模式
  18. Spring AOP表达式报错:Pointcut is not well-formed: expecting &#39;name pattern&#39; at character position
  19. Linux学习笔记 - Shell 控制语句
  20. 【Asp.Net Core】ASP.NET Core 2.0 + EF6 + Linux +MySql混搭

热门文章

  1. DCGAN生成目标训练图片
  2. OPENCV2.46 与2.4.10以上版本的区别
  3. spring的一些annotation
  4. 15.swoole学习笔记--异步写入文件
  5. 六十四、SAP中的内表的9种定义方式
  6. 047-PHP数字前面补零,固定位数补0
  7. 【转】JS字符(字母)与ASCII码转换方法
  8. phpStudy配置站点解决各种不能访问问题(本地可www.xx.com访问)
  9. 吴裕雄--天生自然C++语言学习笔记:C++ 循环
  10. CSU-ACM2020寒假集训比赛2