牛客提高集训营6 C 树(树链剖分)
为了纪(zhuang)念(bi)写完这个树剖单独写一篇。感觉还好,也就6k嘛。
完整比赛题解:https://www.cnblogs.com/SovietPower/p/9826829.html。
肯定要用点来表示边的颜色,然后树剖。
对于操作2,我们可以拆成:
- 将\(u\to v\)路径上的点,所有连向子节点的边染成\(col2\);
- 将\(u\to v\)路径上的点,所有连向父节点的边染成\(col1\);
- 将\(LCA(u,v)\)连向父节点的边染成\(col2\)。
那么本题的操作实际有四种:链修改、链查询、修改一条链上所有连向子节点的边、换根+子树修改。
除了链修改指向子节点的边,其它都是树剖模板(换根见BZOJ3083)。
因为是改所有指向子节点的边,所以也可以树剖维护。用\(to\_son[x]\)表示\(x\)指向儿子的轻边的颜色,用另一棵线段树维护(注意只是轻边,重边要单独在第一棵树上改)。
除此之外,在第一棵线段树上维护每个点\(x\)到\(fa[x]\)的边的颜色。
这样查询时,对于重链可以直接在第一棵线段树上查;对于轻边(\(top[x]\to fa[top[x]]\))的颜色,需要区分是第一棵树上\(top[x]\)的值还是第二棵树上\(fa[top[x]]\)的值。修改时再带一个时间优先级即可。
要修改的链是一样的,且\(to\_son[x]\)只会单点查(切换重链时)(也是单点修改),所以只要一棵线段树同时维护两种标记就行了。
复杂度\(O(m\log^2n)\)。
另外单点查完全可以用非递归。
边界(\(dfn\pm 1\))问题要注意。(会不会还有锅啊。。)
//358ms 20428KB
#include <cstdio>
#include <cctype>
#include <algorithm>
#define fir first
#define sec second
#define mp std::make_pair
#define pr std::pair<int,int>//time val
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5;
int n,Enum,H[N],nxt[N<<1],to[N<<1],fa[N],dep[N],sz[N],son[N],top[N],dfn[N],Index,ref[N],out[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Segment_Tree
{
#define ls rt<<1
#define rs rt<<1|1
#define lson l,m,ls
#define rson m+1,r,rs
#define S N<<2
int cnt[S][3],sz[S];
pr tag1[S],tag2[S];
#undef S
#define Update(rt) cnt[rt][0]=cnt[ls][0]+cnt[rs][0], cnt[rt][1]=cnt[ls][1]+cnt[rs][1], cnt[rt][2]=cnt[ls][2]+cnt[rs][2]
#define Cov1(rt,v) cnt[rt][0]=0, cnt[rt][1]=0, cnt[rt][2]=0, cnt[rt][v.sec]=sz[rt], tag1[rt]=v
#define Cov2(rt,v) tag2[rt]=v
inline void PushDown(int rt)
{
int l=ls,r=rs;
if(tag1[rt].fir) Cov1(l,tag1[rt]), Cov1(r,tag1[rt]), tag1[rt].fir=0;
if(tag2[rt].fir) Cov2(l,tag2[rt]), Cov2(r,tag2[rt]), tag2[rt].fir=0;
}
void Build(int l,int r,int rt)
{
cnt[rt][0]=sz[rt]=r-l+1;
if(l!=r)
{
int m=l+r>>1;
Build(lson), Build(rson);
}
}
void ModifyI(int l,int r,int rt,int L,int R,pr v)//为了常数 粘了仨Modify→_→
{
if(L<=l && r<=R) {Cov1(rt,v); return;}
PushDown(rt);
int m=l+r>>1;
if(L<=m) ModifyI(lson,L,R,v);
if(m<R) ModifyI(rson,L,R,v);
Update(rt);
}
void Modify1(int l,int r,int rt,int p,pr v)
{
if(l==r) {Cov1(rt,v); return;}
PushDown(rt);
int m=l+r>>1;
p<=m ? Modify1(lson,p,v) : Modify1(rson,p,v);
Update(rt);
}
void Modify2(int l,int r,int rt,int L,int R,pr v1,pr v2)
{
if(L<=l && r<=R) {Cov1(rt,v1), Cov2(rt,v2); return;}
PushDown(rt);
int m=l+r>>1;
if(L<=m) Modify2(lson,L,R,v1,v2);
if(m<R) Modify2(rson,L,R,v1,v2);
Update(rt);
}
pr QueryP(int l,int r,int rt,int p,int id)
{
int m;
while(l!=r)
{
PushDown(rt);
m=l+r>>1, p<=m?(r=m,rt=ls):(l=m+1,rt=rs);
}
return id==1?tag1[rt]:tag2[rt];
}
int QueryI(int l,int r,int rt,int L,int R,int c)
{
if(L<=l && r<=R) return cnt[rt][c];
PushDown(rt);
int m=l+r>>1;
if(L<=m)
if(m<R) return QueryI(lson,L,R,c)+QueryI(rson,L,R,c);
else return QueryI(lson,L,R,c);
return QueryI(rson,L,R,c);
}
}T;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
inline void AE(int u,int v)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
inline int Jump(int u,int lca)
{
int las=u;
while(top[u]!=top[lca]) u=fa[las=top[u]];
return u==lca?las:ref[dfn[lca]+1];
}
void DFS1(int x)
{
int mx=0; sz[x]=1;
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa[x])
{
fa[v]=x, dep[v]=dep[x]+1, DFS1(v), sz[x]+=sz[v];
if(sz[v]>mx) mx=sz[v], son[x]=v;
}
}
void DFS2(int x,int tp)
{
top[x]=tp, dfn[x]=++Index, ref[Index]=x;
if(son[x])
{
DFS2(son[x],tp);
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa[x]&&v!=son[x]) DFS2(v,v);
}
out[x]=Index;
}
int Query(int u,int v,int c)
{
int ans=0,tp;
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) std::swap(u,v);
tp=top[u];
if(dfn[tp]<dfn[u]) ans+=T.QueryI(1,n,1,dfn[tp]+1,dfn[u],c);
pr t1=T.QueryP(1,n,1,dfn[tp],1),t2=T.QueryP(1,n,1,dfn[fa[tp]],2);
ans+=t1.fir>t2.fir?(t1.sec==c):(t2.sec==c);
u=fa[tp];
}
if(dep[u]<dep[v]) std::swap(u,v);
if(dfn[v]<dfn[u]) ans+=T.QueryI(1,n,1,dfn[v]+1,dfn[u],c);
return ans;
}
void Modify(int u,int v,int c1,int c2,int t)
{
if(dfn[u]<n) T.Modify1(1,n,1,dfn[u]+1,mp(t,c2));//dfn[son[u]]
if(dfn[v]<n) T.Modify1(1,n,1,dfn[v]+1,mp(t,c2));//这个修改就不会有冲突了
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) std::swap(u,v);
T.Modify2(1,n,1,dfn[top[u]],dfn[u],mp(t+1,c1),mp(t,c2));
u=fa[top[u]];
T.Modify1(1,n,1,dfn[u]+1,mp(t+1,c2));
}
if(dep[u]<dep[v]) std::swap(u,v);
T.Modify2(1,n,1,dfn[v],dfn[u],mp(t+1,c1),mp(t,c2));
T.Modify1(1,n,1,dfn[v],mp(t+1,c2));
}
int main()
{
n=read();
for(int i=1; i<n; ++i) AE(read(),read());
DFS1(1), DFS2(1,1), T.Build(1,n,1);
for(int opt,x,y,c,c2,rt,Q=read(),i=1; i<=Q; ++i)
switch(opt=read())
{
case 1: x=read(),y=read(),printf("%d\n",Query(x,y,read())); break;
case 2: x=read(),y=read(),c=read(),c2=read(),Modify(x,y,c,c2,i<<1); break;
case 3:
{
rt=read(),x=read(),c=read();
if(x==rt) T.ModifyI(1,n,1,1,n,mp(i<<1,c));
else if(dfn[x]<=dfn[rt]&&dfn[rt]<=out[x])
{
y=Jump(rt,x), T.ModifyI(1,n,1,1,dfn[y]-1,mp(i<<1,c));
if(out[y]<n) T.ModifyI(1,n,1,out[y]+1,n,mp(i<<1,c));
}
else if(dfn[x]<out[x]) T.ModifyI(1,n,1,dfn[x]+1,out[x],mp(i<<1,c));
break;
}
}
return 0;
}
牛客提高集训营6 C 树(树链剖分)的更多相关文章
- 牛客国庆集训派对Day6 A Birthday 费用流
牛客国庆集训派对Day6 A Birthday:https://www.nowcoder.com/acm/contest/206/A 题意: 恬恬的生日临近了.宇扬给她准备了一个蛋糕. 正如往常一样, ...
- 2019牛客国庆集训派对day5
2019牛客国庆集训派对day5 I.Strange Prime 题意 \(P=1e10+19\),求\(\sum x[i] mod P = 0\)的方案数,其中\(0 \leq x[i] < ...
- 牛客练习赛11 假的字符串 (Trie树+拓扑找环)
牛客练习赛11 假的字符串 (Trie树+拓扑找环) 链接:https://ac.nowcoder.com/acm/problem/15049 来源:牛客网 给定n个字符串,互不相等,你可以任意指定字 ...
- 2018 牛客国庆集训派对Day4 - H 树链博弈
链接:https://ac.nowcoder.com/acm/contest/204/H来源:牛客网 题目描述 给定一棵 n 个点的树,其中 1 号结点是根,每个结点要么是黑色要么是白色 现在小 Bo ...
- 牛客练习赛28 B数据结构(线段树)
链接:https://www.nowcoder.com/acm/contest/200/B来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言5242 ...
- 牛客国庆集训派对Day4 I-连通块计数(思维,组合数学)
链接:https://www.nowcoder.com/acm/contest/204/I 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 1048576K,其他语言20 ...
- 牛客国庆集训派对Day2 Solution
A 矩阵乘法 思路: 1° 牛客机器太快了,暴力能过. #include <bits/stdc++.h> using namespace std; #define N 5000 in ...
- 牛客练习赛26 E-树上路径 (树链剖分+线段树)
链接:https://ac.nowcoder.com/acm/contest/180/E 来源:牛客网 树上路径 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 262144K,其他语 ...
- 牛客练习赛53E 老瞎眼 pk 小鲜肉(线段树)
链接:https://ac.nowcoder.com/acm/contest/1114/E来源:牛客网题目:老瞎眼有一个长度为 n 的数组 a,为了为难小鲜肉,他准备了 Q 次询问,每次给出 一个区间 ...
随机推荐
- caffe中使用python定义新的层
转载链接:http://withwsf.github.io/2016/04/14/Caffe-with-Python-Layer/ Caffe通过Boost中的Boost.Python模块来支持使用P ...
- Linux驱动开发必看详解神秘内核(完全转载)
Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html IT168 技术文档]在开始步入L ...
- cocos2d-x在App中的应用
cocos2d-x是一个应用广泛的开源游戏引擎,主要是应用与开发2D游戏,开源运行于多个平台,如果只是针对于移动端平台而言,可以运行于android和ios平台. cocos2d-x目前的版本是3.1 ...
- 转载:2.2.5 在配置中使用变量《深入理解Nginx》(陶辉)
原文:https://book.2cto.com/201304/19630.html 有些模块允许在配置项中使用变量,如在日志记录部分,具体示例如下.log_format main '$remot ...
- Android JAR包、Library项目
[JAR包] android引入JAR包,打包成JAR包,打包成Library项目,导入Library项目 (1)项目导入JAR包:1.在项目目录里建立一个libs目录,将外部jar包拷贝在里面.2. ...
- Day5------------系统启动流程
一.引导顺序 BIOS--------------------->MBR-------------------->boot loader------------------------&g ...
- maven 跳过test
-DskipTests,不执行测试用例,但编译测试用例类生成相应的class文件至target/test-classes下. -Dmaven.test.skip=true,不执行测试用例,也不编译测试 ...
- PYTHON-UDP
1.TCP 和 UDP 发送数据时的流程 ***** 解释 为何TCP是可靠的 是因为发送数据后必须收到确认包 2. UDP的模板代码 ***** 1.UDP协议: (数据报协议) 特点: 无连接 优 ...
- java多线程快速入门(一)
1.什么是进程 比如:QQ.QQ游戏.eclipse都是进程,可以通过任务管理器查看进程 2.进程和线程区别 线程是进程的一部分,一个进程可以包含多个线程,一个线程只能属于一个进程 进程是所有线程的集 ...
- LeetCode(50):Pow(x, n)
Medium! 题目描述: 实现 pow(x, n) ,即计算 x 的 n 次幂函数. 示例 1: 输入: 2.00000, 10 输出: 1024.00000 示例 2: 输入: 2.10000, ...