【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/ ...
随机推荐
- 02 Django配置信息
2-1 基本配置信息 import os # 项目路径 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # ...
- Codeforces Round #538 (Div. 2) E 随机数生成
https://codeforces.com/contest/1114/problem/E 题意 交互题,需要去猜一个乱序的等差数列的首项和公差,你能问两种问题 1. 数列中有没有数比x大 2. 数列 ...
- maven下的经常使用的几个元素以及依赖范围的一些知识
maven的pom.xml配置文件里面的project根节点下的dependencies可以包含一个或者多个dependency元素,以声明一个或者多个依赖,每个依赖都可以包含的元素: groupId ...
- Redis集群的主从切换研究
目录 目录 1 1. 前言 1 2. slave发起选举 2 3. master响应选举 5 4. 选举示例 5 5. 哈希槽传播方式 6 6. 一次主从切换记录1 6 6.1. 相关参数 6 6.2 ...
- 返回结点值为e的二叉树指针
题意为,如果二叉树某结点的值为e(假定是整型二叉树),返回这个结点的指针.初看这道题,联想到二叉树可以很简单的遍历,直接返回这个指针不就行了吗?如下图所示,假如要返回值为3的结点指针: 设计好了一个函 ...
- web-day5
第5章WEB05- BootStrap篇 今日任务 使用JQuery完成表单校验 使用BootStrap制作一个响应式页面 使用BootStrap制作网站首页 教学导航 教学目标 掌握什么是响应式及响 ...
- 11:self关键字
本小节知识点: 类方法中的self 对象方法中的self 全局变量成员变量局部变量 self总结 self使用注意 OC提
- hdu 1059
题目大意:就是有价值1.2.3.4.5.6的硬币各多少个,然后让你判断能否把他们分成价值相等的两部分. 题目思路:目测dp,一看果然dp,完全背包,需要剪枝,硬币个数为容量,下标为value,用一个b ...
- JVM可支持的最大线程数
转微博,因为他也是转载 不知道原出处 一.认识问题: 首先我们通过下面这个 测试程序 来认识这个问题:运行的环境 (有必要说明一下,不同环境会有不同的结果):32位 Windows XP,Sun J ...
- LED点阵书写显示
LED点阵书写显示屏 摘要:随着时代的发展,数字电子技术已经普及到我们生活,工作,科研,各个领域,而LED显示以其组构方式灵活.显示稳定.功耗低.寿命长.技术成熟.成本低廉等特点在车站.证券所.运 ...