51nod 1819 黑白树V2(树链剖分)
第一次写如此复杂的树链剖分,
感觉自己代码能力还是挺不错的,没有调试太久(2个小时)
最后代码量高达11K orz(大部分都是重复的线段树代码,以后可以考虑优化一下代码量)
题解:
首先就是要进行一次树链剖分,
然后线段树维护的内容是比较复杂的,核心是这样的
线段树上的一个结点维护以下信息
v[2][2] 第一维代表deep%2,第二维代表是黑还是白(黑是1),v[0][1]意思就是深度为偶数、黑色结点的答案
这里答案是指的维护所有轻儿子子树的答案
num[2][2] 维度代表和上面一样,维护的是所有轻儿子子树中满足该颜色、该深度的结点总数
tag[2] 维度代表deep%2,为1就是代表与当前颜色相反,为0就是相同
ftag[2] 维度依然代表deep%2,永久化标记,维护一个区间被打的永久化标记的情况
这里简单说一下维护的细节和思路吧
1、对于第一个操作修改点的颜色
实际上就是先找到这个点,然后直接修改它的颜色(不打标记),接下来就是不断往上跳,去修改fa[top[x]]的值,因为修改单点颜色会影响它所维护的答案
2、对于第二个操作
也是首先找到这个点,然后给它先打上一个永久化标记,并对它所在的重链打一个懒惰标记。
然后需要去依次修改fa[top[x]]的值,这时我们需要知道它实际改变了多少,所以我们就从底向上统计所有的永久化标记
这样就可以知道实际改变了多少,然后每修改一条重链的点,就删去这条重链上永久化标记的影响,就可以了
3、对于第三个操作
统计答案,要按链来进行
一开始对于第一条重链,首先统计该点x下有多少个结点,得到这个n以后乘x即可,然后在直接在线段树上查询top[x] ~fa[x]这个区间的值,加上即可
然后跳到下一条重链,也是统计新的x有多少个结点,得到这个n以后需要减去son[x]的相应结点个数,再乘x,接下来还是查询top[x]~fa[x],依次进行
注意在跳跃的过程中,也要不断修改永久化标记的影响,这样才会得到真正的答案。
4、对于线段树初始值
要进行一个简单的树型dp,这个比较水就不说了。
写代码的时候也发现到一些问题,比如说最好写赋值型修改,不要返回一个数组指针(会因为局部变量丢失而崩掉)
然后这个代码重复率高的问题也需要解决。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <hash_map>
#define fi first
#define se second
#define pb push_back
using namespace std;
using namespace __gnu_cxx;
typedef pair<int, int> PII;
typedef long long LL;
const int maxn = ;
int deep[maxn], fa[maxn], sz[maxn], son[maxn], top[maxn], down[maxn], c[maxn];
PII dp[maxn][], dps[maxn][];
int tot, n;
vector<int>G[maxn];
hash_map<int, int> H, iH; void print(PII *A){
cout<<A[].fi<<" "<<A[].se<<endl;
cout<<A[].fi<<" "<<A[].se<<endl;
cout<<endl;
}
void print(int *A){
cout<<A[]<<" "<<A[]<<endl; cout<<endl;
} int dfs1(int x){
deep[x] = deep[fa[x]]+;
sz[x] = ;
for(auto to : G[x]){
if(to == fa[x]) continue;
fa[to] = x; sz[x] += dfs1(to);
son[x] = sz[to] > sz[son[x]] ? to : son[x];
}
return sz[x];
}
void dfs2(int x){
iH[H[x] = ++tot] = x;
top[x] = son[fa[x]] == x ? top[fa[x]] : x;
if(son[x]) dfs2(son[x]); down[x] = down[son[x]];
for(auto to : G[x]){
if(to == fa[x] || to == son[x]) continue;
dfs2(to);
}
if(!son[x]) down[x] = x;
}
void dfs3(int x){
int d = deep[x]%;
if(c[x]) dp[x][d].se = ; else dp[x][d].fi = ;
if(c[x]) dps[x][d].se = ; else dps[x][d].fi = ;
for(auto to : G[x]){
if(to == fa[x]) continue;
dfs3(to);
for(int i = ; i < ; i++){
dps[x][i].fi += dps[to][i].fi;
dps[x][i].se += dps[to][i].se;
}
if(to == son[x]) continue;
for(int i = ; i < ; i++) {
dp[x][i].fi += dps[to][i].fi;
dp[x][i].se += dps[to][i].se;
}
}
} void Mulnum(PII D[], PII A[], PII B[]){
PII C[];
for(int i = ; i < ; i++) C[i].fi = A[i].fi + B[i].fi, C[i].se = A[i].se + B[i].se;
for(int i = ; i < ; i++) D[i].fi = C[i].fi, D[i].se = C[i].se;
}
void Multag(int *D, int *A, int *B){
int C[];
for(int i = ; i < ; i++) C[i] = A[i]^B[i];
for(int i = ; i < ; i++) D[i] = C[i];
}
void MulNF(PII *A, int *B){
for(int i = ; i < ; i++) if(B[i]) swap(A[i].fi, A[i].se);
}
LL MulVF(PII *A, int *B){
LL ans = ;
for(int i = ; i < ; i++) ans += B[i] ? A[i].fi : A[i].se;
return ans;
}
void Sub(PII D[], PII A[], PII B[]){
PII C[];
for(int i = ; i < ; i++) C[i].fi = A[i].fi - B[i].fi, C[i].se = A[i].se - B[i].se;
for(int i = ; i < ; i++) D[i].fi = C[i].fi, D[i].se = C[i].se;
} struct Tree{
PII num[], v[];
int tag[];
int ftag[];
}tree[maxn*];
void Puttag(int o, int* tag){
for(int i = ; i < ; i++) if(tag[i]) swap(tree[o].num[i].fi, tree[o].num[i].se);
for(int i = ; i < ; i++) if(tag[i]) swap(tree[o].v[i].fi, tree[o].v[i].se);
Multag(tree[o].tag, tree[o].tag, tag);
}
void Pushdown(int o){
if(tree[o].tag[] == && tree[o].tag[] == ) return;
Puttag(o*, tree[o].tag);
Puttag(o*+, tree[o].tag);
tree[o].tag[] = tree[o].tag[] = ;
}
void Maintain(int o){
Mulnum(tree[o].num, tree[o*].num, tree[o*+].num);
Mulnum(tree[o].v, tree[o*].v, tree[o*+].v);
Multag(tree[o].ftag, tree[o*].ftag, tree[o*+].ftag);
} void Query(int o, int l, int r, int k){
if(l == r){
print(tree[o].v);
print(tree[o].num);
cout<<"******"<<endl;
return;
}
int mid = (l+r)>>; Pushdown(o);
if(k <= mid) Query(o*, l, mid, k);
else Query(o*+, mid+, r, k);
Maintain(o);
} void colchange(int o, int v, PII num[]){
Mulnum(tree[o].num, tree[o].num, num);
for(int i = ; i < ; i++){
tree[o].v[i].se = v*tree[o].num[i].se;
tree[o].v[i].fi = v*tree[o].num[i].fi;
}
} int _Schange(int o, int l, int r, int k){
if(l == r){
int d = deep[iH[k]]%, col = c[iH[k]]; c[iH[k]] ^= ;
col ^= tree[o].tag[d];
PII temp[]; temp[] = temp[] = {, };
if(col) temp[d] = {, -}; else temp[d] = {-, };
colchange(o, iH[k], temp);
return col^;
}
int mid = (l+r)>>, col; Pushdown(o);
if(k <= mid) col = _Schange(o*, l, mid, k);
else col = _Schange(o*+, mid+, r, k);
Maintain(o);
return col;
} void _ftagQuery(int o, int l, int r, int L, int R, int *ans){
if(L <= l && r <= R) {
Multag(ans, ans, tree[o].ftag);
return;
}
int temp[] = {, };
int mid = (l+r)>>; Pushdown(o);
if(L <= mid) _ftagQuery(o*, l, mid, L, R, temp);
if(R > mid) _ftagQuery(o*+, mid+, r, L, R, temp);
Maintain(o);
Multag(ans, ans, temp);
} void _Coladd(int o, int l, int r, int k, PII* num){
if(l == r){
colchange(o, iH[k], num);
return;
}
int mid = (l+r)>>; Pushdown(o);
if(k <= mid) _Coladd(o*, l, mid, k, num);
else _Coladd(o*+, mid+, r, k, num);
Maintain(o);
}
void _numQuery(int o, int l, int r, int L, int R, PII* ans){
if(L <= l && r <= R) {
Mulnum(ans, ans, tree[o].num);
return;
}
int mid = (l+r)>>; Pushdown(o);
PII temp[] = {{, }, {, }};
if(L <= mid) _numQuery(o*, l, mid, L, R, temp);
if(R > mid) _numQuery(o*+, mid+, r, L, R, temp);
Maintain(o);
Mulnum(ans, ans, temp);
}
void _vQuery(int o, int l, int r, int L, int R, PII *ans){
if(L <= l && r <= R) {
Mulnum(ans, ans, tree[o].v);
return;
}
int mid = (l+r)>>; Pushdown(o);
PII temp[] = {{, }, {, }};
if(L <= mid) _vQuery(o*, l, mid, L, R, temp);
if(R > mid) _vQuery(o*+, mid+, r, L, R, temp);
/*cout<<l<<" "<<r<<"-"<<endl;
print(ans);
cout<<"---------"<<endl;*/
Maintain(o);
Mulnum(ans, ans, temp);
}
void _ftagchange(int o, int l, int r, int k, int d){
if(l == r){
tree[o].ftag[d] ^= ;
return;
}
int mid = (l+r)>>; Pushdown(o);
if(k <= mid) _ftagchange(o*, l, mid, k, d);
else _ftagchange(o*+, mid+, r, k, d);
Maintain(o);
}
void _tagchange(int o, int l, int r, int L, int R, int d){
if(L <= l && r <= R){
int temp[] = {, }; temp[d] = ;
Puttag(o, temp);
return;
}
int mid = (l+r)>>; Pushdown(o);
if(L <= mid) _tagchange(o*, l, mid, L, R, d);
if(R > mid) _tagchange(o*+, mid+, r, L, R, d);
Maintain(o);
} int Schange(int o, int l, int r, int k){ return _Schange(o, l, r, H[k]); }
void ftagQuery(int o, int l, int r, int L, int R, int* ans) { _ftagQuery(o, l, r, H[L], H[R], ans);}
void Col_add(int o, int l, int r, int x, PII* num) { _Coladd(o, l, r, H[x], num); }
void numQuery(int o, int l, int r, int L, int R, PII* ans) { _numQuery(o, l, r, H[L], H[R], ans); }
void vQuery(int o, int l, int r, int L, int R, PII* ans) { _vQuery(o, l, r, H[L], H[R], ans); }
void ftagchange(int o, int l, int r, int x, int d) { _ftagchange(o, l, r, H[x], d); }
void tagchange(int o, int l, int r, int L, int R, int d) { _tagchange(o, l, r, H[L], H[R], d);} void T_Insert(int x){
int y = top[x];
int col = Schange(, , n, x), d = deep[x]%;
int ftag[] = {, };
while(y != ){
x = fa[y];
y = top[x];
ftagQuery(, , n, y, x, ftag);
if(ftag[d]) col ^= ;
PII temp[] = {{, }, {, }};
if(col) temp[d] = {-, }; else temp[d] = {, -};
Col_add(, , n, x, temp);
}
} void find_ftag(int x, int *ftag){
ftag[] = ftag[] = ;
if(x == ) return;
int y = top[x];
ftagQuery(, , n, y, x, ftag);
while(y != ){
x = fa[y];
y = top[x];
int temp[] = {, };
ftagQuery(, , n, y, x, temp);
Multag(ftag, ftag, temp);
}
} LL T_Query(int x){
int ftag[] = {, };
find_ftag(fa[top[x]], ftag);
LL ans = ;
PII last[] = {{, }, {, }};
//for(int i = 1; i <= 3; i++) Query(1, 1, n, i);
while(){
PII num[] = {{, }, {, }};
numQuery(, , n, x, down[x], num);
//print(num);
MulNF(num, ftag); Sub(num, num, last);
last[] = last[] = {, };
ans += (num[].se + num[].se)*x;
//cout<<ans<<"*"<<endl;
if(x != top[x]){
PII v[] = {{, }, {, }};
vQuery(, , n, top[x], fa[x], v);
ans += MulVF(v, ftag);
}
//cout<<ans<<"*"<<endl;
numQuery(, , n, top[x], down[x], last);
MulNF(last, ftag);
x = fa[top[x]];
if(x == ) break; ftagQuery(, , n, top[x], x, ftag);
}
return ans;
} void T_Change(int x){
PII num1[] = {{, }, {, }}, num2[] = {{, }, {, }};
//cout<<x<<" "<<down[x]<<endl;
numQuery(, , n, x, down[x], num1);
//print(num1);
ftagchange(, , n, x, (deep[x]+)%);
tagchange(, , n, x, down[x], (deep[x]+)%);
numQuery(, , n, x, down[x], num2);
//print(num2);
int y = top[x];
while(y != ){
x = fa[y];
y = top[x];
int ftag[] = {, };
ftagQuery(, , n, y, x, ftag);
MulNF(num1, ftag); MulNF(num2, ftag);
PII temp[];
for(int i = ; i < ; i++) {
temp[i].fi = num2[i].fi - num1[i].fi;
temp[i].se = num2[i].se - num1[i].se;
}
Col_add(, , n, x, temp);
}
} void Insert(int o, int l, int r, int k, PII *num, LL v){
if(l == r){
for(int i = ; i < ; i++) {
tree[o].num[i].fi = num[i].fi;
tree[o].v[i].fi = num[i].fi*v;
tree[o].num[i].se = num[i].se;
tree[o].v[i].se = num[i].se*v;
}
return;
}
int mid = (l+r)>>;
if(k <= mid) Insert(o*, l, mid, k, num, v);
else Insert(o*+, mid+, r, k, num, v);
Maintain(o);
} int q, x, y;
int main()
{
scanf("%d %d", &n, &q);
for(int i = ; i <= n; i++) scanf("%d", &c[i]);
for(int i = ; i < n; i++){
scanf("%d %d", &x, &y);
G[x].pb(y); G[y].pb(x);
} fa[] = ;
dfs1(); dfs2(); dfs3();
for(int i = ; i <= n; i++) Insert(, , n, H[i], dp[i], i);
//for(int i = 1; i <= n; i++) Query(1, 1, n, i);
while(q--){
scanf("%d %d", &x, &y);
if(x == ){
T_Change(y);
} else if(x == ){
T_Insert(y);
//Query(1, 1, n, H[5]);
} else if(x == ){
printf("%lld\n", T_Query(y));
}
}
return ;
}
51nod 1819 黑白树V2(树链剖分)的更多相关文章
- [FJOI2014]最短路径树问题 长链剖分
[FJOI2014]最短路径树问题 LG传送门 B站传送门 长链剖分练手好题. 如果你还不会长链剖分的基本操作,可以看看我的总结. 这题本来出的很没水平,就是dijkstra(反正我是不用SPFA)的 ...
- 2018.11.03 NOIP模拟 树(长链剖分优化dp)
传送门 考虑直接推式子不用优化怎么做. 显然每一个二进制位分开计算贡献就行. 即记录fi,jf_{i,j}fi,j表示距离iii这个点不超过jjj的点的每个二进制位的0/10/10/1个数. 但直接 ...
- 树链剖分+线段树 HDOJ 4897 Little Devil I(小恶魔)
题目链接 题意: 给定一棵树,每条边有黑白两种颜色,初始都是白色,现在有三种操作: 1 u v:u到v路径(最短)上的边都取成相反的颜色 2 u v:u到v路径上相邻的边都取成相反的颜色(相邻即仅有一 ...
- 树链剖分+线段树 HDOJ 5029 Relief grain(分配粮食)
题目链接 题意: 分粮食我就当成涂色了.有n个点的一棵树,在a到b的路上都涂上c颜色,颜色可重复叠加,问最后每一个点的最大颜色数量的颜色类型. 思路: 首先这题的输出是每一个点最后的情况,考虑离线做法 ...
- (中等) Hiho 1232 Couple Trees(15年北京网络赛F题),主席树+树链剖分。
"Couple Trees" are two trees, a husband tree and a wife tree. They are named because they ...
- 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree
原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...
- 【CodeChef EDGEST】Edges in Spanning Trees(树链剖分+树上启发式合并)
点此看题面 大致题意: 给你两棵\(n\)个点的树,对于第一棵树中的每条边\(e_1\),求存在多少条第二棵树中的边\(e_2\),使得第一棵树删掉\(e_1\)加上\(e_2\).第二棵树删掉\(e ...
- BZOJ3252攻略——长链剖分+贪心
题目描述 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏.今天他得到了一款新游戏<XX 半岛>,这款游戏有n个场景(scene),某 ...
- bzoj 3522 / 4543 [POI 2014] Hotel - 动态规划 - 长链剖分
题目传送门 bzoj 3522 需要root权限的传送点 bzoj 4543 快速的传送点 慢速的传送点 题目大意 给定一棵树,问有多少个无序三元组$(x, y, z)$使得这三个不同点在树上两两距离 ...
随机推荐
- 富文本编辑器 summernote.js
1.引用js 可在 https://summernote.org/ 官网下载 ,并查看详细的API 引入:summernote.js 和 summernote-zh-CN.js 以及样式文件:su ...
- MySQL实现排名并查询指定用户排名功能,并列排名功能
MySQL实现排名并查询指定用户排名功能,并列排名功能 表结构: CREATE TABLE test.testsort ( id int(11) NOT NULL AUTO_INCREMENT, ui ...
- [转]不让iTunes备份到c盘
很多人现在的C盘都是空间不大的SSD硬盘,ITUNES备份老是占越来越大的空间,不如动手把它改成其它盘好了.下面7个步骤教你转移备份. 1.需要一个小工具:Juction.exe,如果你已经是WIN7 ...
- Hadoop(20)-MapReduce框架原理-OutputFormat
1.outputFormat接口实现类 2.自定义outputFormat 步骤: 1). 定义一个类继承FileOutputFormat 2). 定义一个类继承RecordWrite,重写write ...
- python并发编程之多进程、多线程、异步、协程、通信队列Queue和池Pool的实现和应用
什么是多任务? 简单地说,就是操作系统可以同时运行多个任务.实现多任务有多种方式,线程.进程.协程. 并行和并发的区别? 并发:指的是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任 ...
- 《PHP内核探索系列文章》系列分享专栏
<PHP内核探索系列文章>已整理成PDF文档,点击可直接下载至本地查阅 简介 PHP内核探索系列文章收藏夹收藏有关PHP内核方面的知识的文章,对PHP高级进阶的朋友提供PHP内核方面的知识 ...
- 【JDBC】一、JDBC连接数据库
package com.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLExce ...
- 2-Linux C语言指针与内存-学习笔记
Linux C语言指针与内存 前面我们对于: c语言的基本用法 makeFile文件的使用 main函数的详解 标准输入输出流以及错误流管道 工具与原理 指针与内存都是c语言中的要点与难点 指针 数组 ...
- 初步学习pg_control文件之四
接前文,初步学习pg_control文件之三 继续分析 何时出现 DB_SHUTDOWNING状态: 在正常的shutdown的时候,需要进行checkpoint,所以就在此处,设置pg_contr ...
- C++各种类型的简单排序大汇总~
啊,排序的技能点也太多了吧!!!LITTLESUN快要**在排序的技能场了啊!(划掉)经历了两天48小时2880分钟172800秒的艰苦奋斗,终于终于终于学的差不多了!明天就可以去打排序的小怪喽!(撒 ...