【xsy2913】 enos 动态dp
题目大意:给你一棵 $n$个点 以 $1$为根 的树,每个点有$ 0,1,2 $三种颜色之一,初始时整棵树的颜色均为 $0$。
$m$ 次操作, 每次操作形如:
1 x y c : 将 $x$到$y$的路径上的点全部改为颜色$C$
2 x : 询问 $x$ 所在的同色连通块大小
数据范围:$n,m≤10^5$。
此题一眼动态dp
首先我们先列出正常的dp式子
设$f[u]$表示以$u$为根的子树中,$u$所在的同色联通块大小
显然,$f[u]=1+\sum_{v∈son[u],col[u]=col[v]} f[v]$
若需要输出某个点$x$的答案,我们需要输出$f[y]$,其中$y$是满足$x$至$y$颜色相同的最远祖先。
下面考虑动态dp
我们用树链剖分把原树剖成若干条链,每条链用线段树维护,对于线段树每个节点,我们维护七个变量:
设线段树某个节点表示的区间为$[l_k,r_k]$,这个区间中对应原树的节点编号为$rec[l_k],rec[l_k+1]....rec[r_k]$。
$sum[i]$($0≤i≤2$)表示当前的区间中,颜色为$i$的节点的个数。
$cnt[i]$($0≤i≤2$)表示原树中所有与$rec[l_k....r_k]$相连的,且颜色为$i$的轻儿子中,满足以这些儿子为根的子树中,这些点所在的同色联通快大小之和。
$tag$为涂色标记,表示区间$[l_k,r_k]$中的点是否被刷成了同一个颜色。
首先考虑查询$x$所在的联通块大小,令$id[x]$表示节点x在原树中的$dfs$序,$col[x]$表示第$x$个节点当前的颜色,$top[x]$表示$x$所在重链链顶节点编号
我们首先在$x$所在的重链上,查询出$L$和$R$,满足$L≤x≤R$,且节点$rec[L],rec[L+1]...rec[x]...rec[R]$的颜色是一样的,答案显然要累加上这一段节点的贡献。
查询这部分的贡献我们可以在线段树上向x两侧二分查找即可,详见代码。
然后我们需要加上所有与区间$[L,R]$相连的轻链上的贡献。查询这部分信息直接在线段树上将$cnt$的信息累加一下就可以了。
我们目前只统计了x所在重链的情况,上方的重链我们尚未统计
若$rec[L]=top[x]$,说明$x$所在的联通块可能会与$top[x]$上方的节点相连,这个时候需要去统计下上方的贡献。
否则直接退出就可以了。
下面考虑修改操作
我们按照正常树剖的操作来处理,假设我们当前要更改$[x',x]$的颜色
我们显然现在线段树上对这一个区间更新一下颜色。
然后我们发现,这么操作的话,以$top[x]$为根的树种,$top[x]$所在同色联通块大小可能会变,这个时候我们需要重新求一下“以$top[x]$为根,$top[x]$所在同色联通块大小”,并将这个值上传更新上一条链的cnt值。
(详见代码)
这里有一个要注意的地方,更新区间的颜色可以只更新到$lca(x,y)$,但是更新联通块大小必须更新到根(场上错在这里)
然后就没有了
注意细节!
#include<bits/stdc++.h>
#define M 100005
#define mid ((a[x].l+a[x].r)>>1)
using namespace std; struct seg{int l,r,tag,sum[],cnt[];}a[M<<]={};
void build(int x,int l,int r){
a[x].l=l; a[x].r=r; a[x].tag=-; if(l==r) return;
build(x<<,l,mid); build(x<<|,mid+,r);
}
void upd(int x,int k){
a[x].sum[]=a[x].sum[]=a[x].sum[]=;
a[x].tag=k; a[x].sum[k]=a[x].r-a[x].l+;
}
void pushdown(int x){
if(a[x].tag!=-) upd(x<<,a[x].tag),upd(x<<|,a[x].tag);
a[x].tag=-;
}
void pushup(int x){
for(int i=;i<;i++){
a[x].sum[i]=a[x<<].sum[i]+a[x<<|].sum[i];
a[x].cnt[i]=a[x<<].cnt[i]+a[x<<|].cnt[i];
}
}
void updatacol(int x,int l,int r,int col){
if(l<=a[x].l&&a[x].r<=r) return upd(x,col);
pushdown(x);
if(l<=mid) updatacol(x<<,l,r,col);
if(mid<r) updatacol(x<<|,l,r,col);
pushup(x);
}
void updatacnt(int x,int id,int col,int val){
if(a[x].l==a[x].r)return void(a[x].cnt[col]+=val);
pushdown(x);
if(id<=mid) updatacnt(x<<,id,col,val);
else updatacnt(x<<|,id,col,val);
pushup(x);
}
int querycnt(int x,int l,int r,int col){
if(l<=a[x].l&&a[x].r<=r) return a[x].cnt[col];
pushdown(x); int res=;
if(l<=mid) res+=querycnt(x<<,l,r,col);
if(mid<r) res+=querycnt(x<<|,l,r,col);
return res;
}
int querycol(int x,int id){
if(a[x].l==a[x].r){
if(a[x].sum[]) return ;
if(a[x].sum[]) return ;
return ;
}
pushdown(x);
if(id<=mid) return querycol(x<<,id);
return querycol(x<<|,id);
}
int queryUP(int x,int l,int r,int col){
if(l<=a[x].l&&a[x].r<=r){
if(a[x].sum[col]==a[x].r-a[x].l+) return a[x].l;
}
if(a[x].l==a[x].r) return ;
pushdown(x);
if(mid<r){
int res=queryUP(x<<|,l,r,col);
if(res!=mid+) return res;
int res2=;
if(l<=mid) res2=queryUP(x<<,l,r,col);
if(res2) return res2;
return res;
}
return queryUP(x<<,l,r,col);
}
int queryDN(int x,int l,int r,int col){
if(l<=a[x].l&&a[x].r<=r){
if(a[x].sum[col]==a[x].r-a[x].l+) return a[x].r;
}
if(a[x].l==a[x].r) return ;
pushdown(x);
if(l<=mid){
int res=queryDN(x<<,l,r,col);
if(res!=mid) return res;
int res2=;
if(mid<r) res2=queryDN(x<<|,l,r,col);
if(res2) return res2;
return res;
}
return queryDN(x<<|,l,r,col);
} struct edge{int u,next;}e[M]={}; int head[M]={},use=;
void add(int x,int y){use++;e[use].u=y;e[use].next=head[x];head[x]=use;}
int fa[M]={},id[M]={},siz[M]={},son[M]={},dep[M]={},top[M]={},dn[M]={},t=,n,m;
int last[M]={},rec[M]={}; void dfs(int x){
siz[x]=;
for(int i=head[x];i;i=e[i].next){
fa[e[i].u]=x; dep[e[i].u]=dep[x]+;
dfs(e[i].u);
siz[x]+=siz[e[i].u];
if(siz[son[x]]<siz[e[i].u]) son[x]=e[i].u;
}
}
void dfs(int x,int Top){
top[x]=Top; id[x]=++t; rec[t]=x;
if(son[x]){
dfs(son[x],Top);
dn[x]=dn[son[x]];
}else{
updatacol(,id[Top],id[x],);
dn[x]=x;
}
for(int i=head[x];i;i=e[i].next) if(e[i].u!=son[x]){
dfs(e[i].u,e[i].u);
updatacnt(,id[x],,last[e[i].u]=siz[e[i].u]);
}
} int Query(int x,int col){
if(!x) return ;
int l=id[top[x]],r=id[dn[x]];
int L=queryUP(,l,id[x],col);
int R=queryDN(,id[x],r,col);
int res=(R-L+)+querycnt(,L,R,col);
if(rec[L]==top[x]&&fa[top[x]]&&querycol(,id[fa[top[x]]])==col)
return Query(fa[top[x]],col);
return res;
} void Updata(int x,int y,int col,int chg){
if(!x) return;
int Lastcol;
if(top[x]==top[y]){
if(dep[x]<dep[y]) swap(x,y);
Lastcol=querycol(,id[top[x]]);
if(chg) updatacol(,id[y],id[x],col);
}else{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
Lastcol=querycol(,id[top[x]]);
if(chg) updatacol(,id[top[x]],id[x],col);
}
int Topcol=querycol(,id[top[x]]);
int L=id[top[x]];
int R=queryDN(,L,id[dn[x]],Topcol);
int val=(R-L+)+querycnt(,L,R,Topcol);
if(fa[top[x]]){
updatacnt(,id[fa[top[x]]],Lastcol,-last[top[x]]);
updatacnt(,id[fa[top[x]]],Topcol,val);
}
last[top[x]]=val;
if(top[x]!=top[y]) Updata(fa[top[x]],y,col,);
else Updata(fa[top[x]],fa[top[x]],col,);
} int main(){
scanf("%d%d",&n,&m);
build(,,n);
for(int i=,x;i<=n;i++) scanf("%d",&x),add(x,i);
dfs(); dfs(,);
while(m--){
int op,x,y,col; scanf("%d%d",&op,&x);
if(op==) printf("%d\n",Query(x,querycol(,id[x])));
else{
scanf("%d%d",&y,&col);
Updata(x,y,col,);
}
}
}
【xsy2913】 enos 动态dp的更多相关文章
- 动态DP之全局平衡二叉树
目录 前置知识 全局平衡二叉树 大致介绍 建图过程 修改过程 询问过程 时间复杂度的证明 板题 前置知识 在学习如何使用全局平衡二叉树之前,你首先要知道如何使用树链剖分解决动态DP问题.这里仅做一个简 ...
- Luogu P4643 【模板】动态dp
题目链接 Luogu P4643 题解 猫锟在WC2018讲的黑科技--动态DP,就是一个画风正常的DP问题再加上一个动态修改操作,就像这道题一样.(这道题也是PPT中的例题) 动态DP的一个套路是把 ...
- 动态dp学习笔记
我们经常会遇到一些问题,是一些dp的模型,但是加上了什么待修改强制在线之类的,十分毒瘤,如果能有一个模式化的东西解决这类问题就会非常好. 给定一棵n个点的树,点带点权. 有m次操作,每次操作给定x,y ...
- 洛谷P4719 动态dp
动态DP其实挺简单一个东西. 把DP值的定义改成去掉重儿子之后的DP值. 重链上的答案就用线段树/lct维护,维护子段/矩阵都可以.其实本质上差不多... 修改的时候在log个线段树上修改.轻儿子所在 ...
- 动态 DP 学习笔记
不得不承认,去年提高组 D2T3 对动态 DP 起到了良好的普及效果. 动态 DP 主要用于解决一类问题.这类问题一般原本都是较为简单的树上 DP 问题,但是被套上了丧心病狂的修改点权的操作.举个例子 ...
- 动态dp初探
动态dp初探 动态区间最大子段和问题 给出长度为\(n\)的序列和\(m\)次操作,每次修改一个元素的值或查询区间的最大字段和(SP1714 GSS3). 设\(f[i]\)为以下标\(i\)结尾的最 ...
- [总结] 动态DP学习笔记
学习了一下动态DP 问题的来源: 给定一棵 \(n\) 个节点的树,点有点权,有 \(m\) 次修改单点点权的操作,回答每次操作之后的最大带权独立集大小. 首先一个显然的 \(O(nm)\) 的做法就 ...
- UOJ268 [清华集训2016] 数据交互 【动态DP】【堆】【树链剖分】【线段树】
题目分析: 不难发现可以用动态DP做. 题目相当于是要我求一条路径,所有与路径有交的链的代价加入进去,要求代价最大. 我们把链的代价分成两个部分:一部分将代价加入$LCA$之中,用$g$数组保存:另一 ...
- [复习]动态dp
[复习]动态dp 你还是可以认为我原来写的动态dp就是在扯蛋. [Luogu4719][模板]动态dp 首先作为一个\(dp\)题,我们显然可以每次修改之后都进行暴力\(dp\),设\(f[i][0/ ...
随机推荐
- 2018.12.15 poj3415 Common Substrings(后缀自动机)
传送门 后缀自动机基础题. 给两个字符串,让你求长度不小于kkk的公共子串的数量. 这题可以用后缀自动机解决废话 考虑对其中一个字串建出后缀自动机,然后用另一个在上面跑,注意到如果一个状态有贡献的话, ...
- 2018.10.24 bzoj3195: [Jxoi2012]奇怪的道路(状压dp)
传送门 f[i][j][k]f[i][j][k]f[i][j][k]表示前iii个点连了jjj条边,第i−K+1i-K+1i−K+1~iii个点连边数的奇偶性为kkk时的方案数. 转移规定只能从后向前 ...
- JPA错误
2016-11-141.2016-10-31: hibernate用注解 一对多 报Could not determine type for错误 原因: 接下来继续解决第二个问题:怎么又与集合打交道 ...
- (18)What a planet needs to sustain life
https://www.ted.com/talks/dave_brain_what_a_planet_needs_to_sustain_life/transcript 00:12I'm really ...
- C#创建、设置和安装Windows服务
文章大部分内容转自:http://www.cnblogs.com/greatandforever/archive/2008/10/14/1310504.html:和:http://www.cnblog ...
- Verilog中的阻塞与非阻塞
这篇文档值得阅读 按说阻塞与非阻塞是Verilog中最基本的东西,也是老生常谈.但是最近看到很多程序里用到阻塞语句竟然不是很明白,说到底是从来没有自己仔细分析过.当然一般情况程序中也是推荐用非阻塞的. ...
- ArcGIS API for Silverlight/ 开发入门 环境搭建
Silverlight/ 开发入门 环境搭建1 Silverlight SDK下载ArcGIS API for Microsoft Silverlight/WPF ,需要注册一个ESRI Gloab ...
- C#-VS支持的语言
其中C语言选C++
- uva12298(生成函数)
生成函数的一般应用: #include<iostream> #include<cstring> #include<cmath> #include<cstdio ...
- POJ3723--Conscription(MST)WRONG
Description Windy has a country, and he wants to build an army to protect his country. He has picked ...