[BZOJ4817][SDOI2017]树点涂色(LCT+DFS序线段树)
4817: [Sdoi2017]树点涂色
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 692 Solved: 408
[Submit][Status][Discuss]Description
Bob有一棵n个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。Bob可能会进行这几种操作:1 x:把点x到根节点的路径上所有的点染上一种没有用过的新颜色。2 x y:求x到y的路径的权值。3 x y:在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。Bob一共会进行m次操作Input
第一行两个数n,m。接下来n-1行,每行两个数a,b,表示a与b之间有一条边。接下来m行,表示操作,格式见题目描述1<=n,m<=100000Output
每当出现2,3操作,输出一行。如果是2操作,输出一个数表示路径的权值如果是3操作,输出一个数表示权值的最大值Sample Input
5 6
1 2
2 3
3 4
3 5
2 4 5
3 3
1 4
2 4 5
1 5
2 4 5Sample Output
3
4
2
2HINT
Source
首先发现第一个操作很像LCT里的Access(),这个方向已经对了。
我们设f[x]表示x与父节点的值是否一样,且f[1]=1,那么对于每个点的答案就是这个点到根上的所有f[x]之和,设为g[x]。
2操作可以转化成g[x]+g[y]-2*g[lca]+1(这里的+1要考虑清楚),3操作可以转化成求x子树中的g[]的最大值,考虑如何维护g[]。
可以发现从x修改到根时,只有每次发生链的切换的时候f[]值才会改变,而每次链的切换正是LCT中Access()所执行的操作,所以我们每次Access()切换链的时候,将其子树的所有g[]加1,对新接进来的节点的子树g[]减1,这个涉及子树的操作,直接DFS+线段树解决即可。
写代码的时候突然发现自己不会区间修改区间查询的线段树了。。吃枣药丸。。
说一下标记永久化的事情,一般的标记是存放自顶向下的懒惰信息,标记自顶向下逐层下放,标记永久化则是可以看作存的是仅在这一层的懒惰信息,标记并不下放而是自底向上地合并。
一般来说标记永久化可能会短一点,速度也会快一些,但并不是非常直观,所以在不卡常数的情况下还是写普通标记吧。
先贴一份没有标记永久化的片段
void push(int x,int L,int R){
if (!tag[x]) return;
tag[ls]+=tag[x]; mx[ls]+=tag[x];
tag[rs]+=tag[x]; mx[rs]+=tag[x];
tag[x]=;
}
void ins(int x,int L,int R,int l,int r,int k){
if (L==l && r==R){ tag[x]+=k; mx[x]+=k; return; }
int mid=(L+R)>>; push(x,L,R);
if (r<=mid) ins(ls,L,mid,l,r,k);
else if (l>mid) ins(rs,mid+,R,l,r,k);
else ins(ls,L,mid,l,mid,k),ins(rs,mid+,R,mid+,r,k);
mx[x]=max(mx[ls],mx[rs]);
}
int ask(int x,int L,int R,int l,int r){
if (L==l && r==R) return mx[x];
int mid=(L+R)>>; push(x,L,R);
if (r<=mid) return ask(ls,L,mid,l,r);
else if (l>mid) return ask(rs,mid+,R,l,r);
else return max(ask(ls,L,mid,l,mid),ask(rs,mid+,R,mid+,r));
}
下面是标记永久化的程序,比原来快1/5。
#include<cstdio>
#include<algorithm>
#define ls (x<<1)
#define rs ((x<<1)|1)
#define rep(i,l,r) for (int i=l; i<=r; i++)
#define For(i,x) for (int i=h[x],k; i; i=nxt[i])
using namespace std; const int N=;
int n,m,x,y,op,cnt,nd,tim,val[N],top[N],son[N],to[N],h[N],nxt[N];
int sz[N],d[N],L[N],R[N],ch[N][],mx[N],tag[N],f[N],fa[N];
void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
bool isroot(int x){ return (!f[x]) || (ch[f[x]][]!=x && ch[f[x]][]!=x); } void dfs(int x,int pre){
sz[x]=; d[x]=d[pre]+;
For(i,x) if ((k=to[i])!=pre){
f[k]=fa[k]=x; dfs(k,x); sz[x]+=sz[k];
if (sz[son[x]]<sz[k]) son[x]=k;
}
} void dfs2(int x,int tp){
L[x]=++tim; top[x]=tp; val[tim]=d[x];
if (son[x]) dfs2(son[x],tp);
For(i,x) if ((k=to[i])!=fa[x] && k!=son[x]) dfs2(k,k);
R[x]=tim;
} void rot(int x){
int y=f[x],z=f[y],w=ch[y][]==x;
if (!isroot(y)) ch[z][ch[z][]==y]=x;
f[x]=z; f[y]=x; f[ch[x][w^]]=y;
ch[y][w]=ch[x][w^]; ch[x][w^]=y;
} void splay(int x){
while (!isroot(x)){
int y=f[x],z=f[y];
if (!isroot(y)){
if ((ch[z][]==y)^(ch[y][]==x)) rot(x); else rot(y);
}
rot(x);
}
} void ins(int x,int L,int R,int l,int r,int k){
if (L==l && r==R){ tag[x]+=k; mx[x]+=k; return; }
int mid=(L+R)>>;
if (r<=mid) ins(ls,L,mid,l,r,k);
else if (l>mid) ins(rs,mid+,R,l,r,k);
else ins(ls,L,mid,l,mid,k),ins(rs,mid+,R,mid+,r,k);
mx[x]=max(mx[ls],mx[rs])+tag[x];
} int ask(int x,int L,int R,int l,int r){
if (L==l && r==R) return mx[x];
int mid=(L+R)>>;
if (r<=mid) return tag[x]+ask(ls,L,mid,l,r);
else if (l>mid) return tag[x]+ask(rs,mid+,R,l,r);
else return tag[x]+max(ask(ls,L,mid,l,mid),ask(rs,mid+,R,mid+,r));
} int find(int x){ while (ch[x][]) x=ch[x][]; return x; } void access(int x){
for (int y=; x; y=x,x=f[x]){
splay(x); int t=find(ch[x][]);
if (t) ins(,,n,L[t],R[t],);
t=find(y); ch[x][]=y;
if (t) ins(,,n,L[t],R[t],-);
}
} void build(int x,int L,int R){
if (L==R) { mx[x]=val[L]; return; }
int mid=(L+R)>>;
build(ls,L,mid); build(rs,mid+,R);
mx[x]=max(mx[ls],mx[rs]);
} int get(int u,int v){
for (; top[u]!=top[v]; u=fa[top[u]])
if (d[top[u]]<d[top[v]]) swap(u,v);
return (d[u]<d[v])?u:v;
} int main(){
freopen("paint.in","r",stdin);
freopen("paint.out","w",stdout);
scanf("%d%d",&n,&m);
rep(i,,n) scanf("%d%d",&x,&y),add(x,y),add(y,x);
dfs(,); dfs2(,); build(,,n);
while (m--){
scanf("%d",&op);
if (op==) scanf("%d",&x),access(x);
else if (op==){
scanf("%d%d",&x,&y); int lca=get(x,y);
printf("%d\n",ask(,,n,L[x],L[x])+ask(,,n,L[y],L[y])-*ask(,,n,L[lca],L[lca])+);
}else scanf("%d",&x),printf("%d\n",ask(,,n,L[x],R[x]));
}
return ;
}
[BZOJ4817][SDOI2017]树点涂色(LCT+DFS序线段树)的更多相关文章
- bzoj4817/luogu3703 树点涂色 (LCT+dfs序+线段树)
我们发现,这个染色的操作他就很像LCT中access的操作(为什么??),然后就自然而然地想到,其实一个某条路径上的颜色数量,就是我们做一个只有access操作的LCT,这条路径经过的splay的数量 ...
- BZOJ.4817.[SDOI2017]树点涂色(LCT DFS序 线段树)
题目链接 操作\(1.2\)裸树剖,但是操作\(3\)每个点的答案\(val\)很不好维护.. 如果我们把同种颜色的点划分到同一连通块中,那么向根染色的过程就是Access()! 最初所有点间都是虚边 ...
- P3703 [SDOI2017]树点涂色 LCT维护颜色+线段树维护dfs序+倍增LCA
\(\color{#0066ff}{ 题目描述 }\) Bob有一棵\(n\)个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同. 定义一条路径的权值是:这条路径上的点 ...
- [BZOJ3779]重组病毒(LCT+DFS序线段树)
同[BZOJ4817]树点涂色,只是多了换根操作,分类讨论下即可. #include<cstdio> #include<algorithm> #define lc ch[x][ ...
- [BZOJ4817][SDOI2017]树点涂色:Link-Cut Tree+线段树
分析 与[BZOJ3779]重组病毒唯一的区别是多了一个链上求实链段数的操作. 因为每条实链的颜色必然不相同且一条实链上不会有两个深度相同的点(好像算法的正确性和第二个条件没什么关系,算了算了),画图 ...
- BZOJ 4817 [SDOI2017]树点涂色 (LCT+线段树维护dfs序)
题目大意:略 涂色方式明显符合$LCT$里$access$操作的性质,相同颜色的节点在一条深度递增的链上 用$LCT$维护一个树上集合就好 因为它维护了树上集合,所以它别的啥都干不了了 发现树是静态的 ...
- Educational Codeforces Round 6 E dfs序+线段树
题意:给出一颗有根树的构造和一开始每个点的颜色 有两种操作 1 : 给定点的子树群体涂色 2 : 求给定点的子树中有多少种颜色 比较容易想到dfs序+线段树去做 dfs序是很久以前看的bilibili ...
- 【BZOJ-3252】攻略 DFS序 + 线段树 + 贪心
3252: 攻略 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 339 Solved: 130[Submit][Status][Discuss] D ...
- Codeforces 343D Water Tree(DFS序 + 线段树)
题目大概说给一棵树,进行以下3个操作:把某结点为根的子树中各个结点值设为1.把某结点以及其各个祖先值设为0.询问某结点的值. 对于第一个操作就是经典的DFS序+线段树了.而对于第二个操作,考虑再维护一 ...
随机推荐
- 【BZOJ】1497: [NOI2006]最大获利 最大权闭合子图或最小割
[题意]给定n个点,点权为pi.m条边,边权为ci.选择一个点集的收益是在[点集中的边权和]-[点集点权和],求最大获利.n<=5000,m<=50000,0<=ci,pi<= ...
- select多选框
select多选框 效果: 代码: <HTML> <HEAD> <TITLE>选择下拉菜单</TITLE> <meta http-equiv=&q ...
- laravel 模糊查询
模糊查询: Model::where('field_name','like','%'.$keywords.'%')->get() 转载:http://wenda.golaravel.com/qu ...
- let块级作用域
let是es6中新加的作用域,即块级作用域. var申明的变量要么全局,要么函数级,而let允许把变量的作用域限制在块级域中,这里的块级可以是()内,或{}内. 示例: code_1: "u ...
- oracle数据库的date和timestamp类型
1.date类型存储数据的格式为年月日时分秒,可以精确到秒 timestamp类型存储数据的格式为年月日时分秒,可以精确到纳秒(9位) 2.date类型 Date类型的数据可以显示到年月日,也可以显示 ...
- Metlnfo CMS全版本漏洞收集
根据https://www.seebug.org/appdir/MetInfo 进行书写. [版本:Metlnfo 4.0] 漏洞标题:Metlnfo cms任意用户密码修改 漏洞文件:member/ ...
- 在Perl中使用Getopt::Long模块来接收用户命令行参数
我们在linux常常用到一个程序需要加入参数,现在了解一下perl中的有关控制参数的函数.getopt.在linux有的参数有二种形式.一种是–help,另一种是-h.也就是-和–的分别.–表示完整参 ...
- Redis 启动警告解决【转】
[root@centos224]# service redisd start :M Nov :: (it was originally set to ). _._ _.-``__ ''-._ _.-` ...
- juery下拉刷新,div加载更多元素并添加点击事件(二)
buffer.append("<div class='col-xs-3 "+companyId+"' style='padding-left: 10px; padd ...
- Android 开发笔记(一) 按钮事件调用Activity
UI创建按钮及事件 Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button);mEmailSignInB ...