【BZOJ4817】【SDOI2017】树点涂色 [LCT][线段树]
树点涂色
Time Limit: 10 Sec Memory Limit: 128 MB
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
1 2
2 3
3 4
3 5
2 4 5
3 3
1 4
2 4 5
1 5
2 4 5
Sample Output
4
2
2
HINT
Solution
我们将边两端的点颜色相同的边设为实边,不同的设为虚边。那么一次新增颜色的操作显然就是LCT的access操作!access的时候恰是虚边和实边的转换。
那么我们只要用线段树维护每个点到根的贡献,结合dfs序来实现子树加,每次在LCT进行access的时候进行+-1修改,然后询问的时候用区间求和,区间最值求得答案即可。
Code
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std;
typedef long long s64; const int ONE = 2e5+; int n,m;
int x,y,P;
int POS[ONE],POSCNT;
int pos[ONE],dfn_cnt,size[ONE],dfn[ONE];
int Dep[ONE],son[ONE],Top[ONE];
int lc[ONE],rc[ONE],fa[ONE],fat[ONE];
int res_max,res_value; inline int get()
{
int res=,Q=; char c;
while( (c=getchar())< || c>)
if(c=='-')Q=-;
if(Q) res=c-;
while((c=getchar())>= && c<=)
res=res*+c-;
return res*Q;
} namespace tree
{
int next[ONE],first[ONE],go[ONE],tot=; void Add(int u,int v)
{
next[++tot]=first[u]; first[u]=tot; go[tot]=v;
next[++tot]=first[v]; first[v]=tot; go[tot]=u;
} void Dfs(int u,int father)
{
pos[u] = ++dfn_cnt; dfn[dfn_cnt] = u;
size[u] = ;
Dep[u] = Dep[father] + ;
for(int e=first[u];e;e=next[e])
{
int v=go[e];
if(v==father) continue;
fa[v] = u; fat[v] = u;
Dfs(v,u);
size[u] += size[v];
if(size[v] > size[son[u]]) son[u] = v;
}
} void Dfs_twice(int u,int father)
{
POS[u] = ++POSCNT;
if(son[u])
{
int v=son[u];
Top[v] = Top[u];
Dfs_twice(v,u);
} for(int e=first[u];e;e=next[e])
{
int v=go[e];
if(v==father || v==son[u]) continue;
Top[v] = v;
Dfs_twice(v,u);
}
} int LCA(int x,int y)
{
while(Top[x]!=Top[y])
{
if( Dep[Top[x]] < Dep[Top[y]] ) swap(x,y);
x = fat[Top[x]];
}
if(POS[x] > POS[y]) swap(x,y);
return x;
}
} namespace Seg
{
struct power
{
int add,value;
int maxx;
}Node[ONE*]; void pushdown(int i,int Q)
{
if(Node[i].add)
{
Node[i<<].add += Node[i].add;
Node[i<<|].add += Node[i].add;
Node[i<<].maxx += Node[i].add;
Node[i<<|].maxx += Node[i].add;
Node[i<<].value += Node[i].add * (Q-Q/);
Node[i<<|].value += Node[i].add * (Q/);
Node[i].add = ;
}
} void Build(int i,int l,int r)
{
if(l==r)
{
Node[i].value = Dep[dfn[l]];
Node[i].maxx = Dep[dfn[l]];
return ;
}
int mid = l+r>>;
Build(i<<,l,mid); Build(i<<|,mid+,r);
Node[i].value = Node[i<<].value + Node[i<<|].value;
Node[i].maxx = max(Node[i<<].maxx, Node[i<<|].maxx);
} void Update(int i,int l,int r,int L,int R,int x)
{
if(L<=l && r<=R)
{
Node[i].add += x;
Node[i].value += (r-l+)*x;
Node[i].maxx += x;
return;
}
pushdown(i,r-l+);
int mid = l+r>>;
if(L<=mid) Update(i<<,l,mid,L,R,x);
if(mid+<=R) Update(i<<|,mid+,r,L,R,x); Node[i].value = Node[i<<].value + Node[i<<|].value;
Node[i].maxx = max(Node[i<<].maxx , Node[i<<|].maxx);
} void Query(int i,int l,int r,int L,int R)
{
if(L<=l && r<=R)
{
res_max = max(res_max,Node[i].maxx);
res_value += Node[i].value;
return;
}
pushdown(i,r-l+);
int mid = l+r>>;
if(L<=mid) Query(i<<,l,mid,L,R);
if(mid+<=R) Query(i<<|,mid+,r,L,R);
}
} namespace LCT
{
int is_real(int x)
{
return (lc[fa[x]]==x || rc[fa[x]]==x);
} void Turn(int x)
{
int y = fa[x], z = fa[y];
int b = x==lc[y] ? rc[x]:lc[x]; fa[x] = z; fa[y] = x;
if(b) fa[b] = y; if(z)
{
if(y == lc[z]) lc[z] = x;
else
if(y == rc[z]) rc[z] = x;
} if(x==lc[y]) rc[x]=y,lc[y]=b;
else lc[x]=y,rc[y]=b;
} void Splay(int x)
{
while(is_real(x))
{
if(is_real(fa[x]))
{
if( (lc[fa[x]]==x) == (lc[fa[fa[x]]]==fa[x])) Turn(fa[x]);
else Turn(x);
}
Turn(x);
}
} int find_root(int x)
{
while(lc[x]) x=lc[x];
return x;
} void access(int x)
{
for(int p=x,q=; p; q=p,p=fa[p])
{
Splay(p);
if(rc[p])
{
int N = find_root(rc[p]);
Seg::Update(,,n,pos[N],pos[N]+size[N]-,);
} rc[p] = q;
if(rc[p])
{
int N = find_root(rc[p]);
Seg::Update(,,n,pos[N],pos[N]+size[N]-,-);
}
}
}
} int Getsum(int x,int y)
{
int Ans, Sx, Sy, SLCA, LCA;
LCA = tree::LCA(x,y);
x=pos[x], y=pos[y], LCA=pos[LCA];
res_value = ; Seg::Query(,,n,x,x); Sx = res_value;
res_value = ; Seg::Query(,,n,y,y); Sy = res_value;
res_value = ; Seg::Query(,,n,LCA,LCA); SLCA = res_value;
return Sx+Sy-*SLCA+;
} int Getmax(int x)
{
res_max = ;
Seg::Query(,,n,pos[x],pos[x]+size[x]-);
return res_max;
} int main()
{
n=get(); m=get();
for(int i=;i<=n-;i++)
{
x=get(); y=get();
tree::Add(x,y);
} tree::Dfs(,);
Top[] = , tree::Dfs_twice(,);
Seg::Build(,,n); while(m--)
{
P = get(); x=get();
if(P==)
LCT::access(x);
if(P==)
y=get(), printf("%d\n",Getsum(x,y));
if(P==)
printf("%d\n",Getmax(x));
}
}
【BZOJ4817】【SDOI2017】树点涂色 [LCT][线段树]的更多相关文章
- 【BZOJ4817】[Sdoi2017]树点涂色 LCT+线段树
[BZOJ4817][Sdoi2017]树点涂色 Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路径的权值是:这条路 ...
- [Sdoi2017]树点涂色 [lct 线段树]
[Sdoi2017]树点涂色 题意:一棵有根树,支持x到根染成新颜色,求x到y颜色数,求x子树里点到根颜色数最大值 考场发现这个信息是可减的,但是没想到lct 特意设计成lct的形式! 如何求颜色数? ...
- [SDOI2017][bzoj4817] 树点涂色 [LCT+线段树]
题面 传送门 思路 $LCT$ 我们发现,这个1操作,好像非常像$LCT$里面的$Access$啊~ 那么我们尝试把$Access$操作魔改成本题中的涂色 我们令$LCT$中的每一个$splay$链代 ...
- BZOJ4817[Sdoi2017]树点涂色——LCT+线段树
题目描述 Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色.Bob可能会进 ...
- bzoj4817 & loj2001 [Sdoi2017]树点涂色 LCT + 线段树
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4817 https://loj.ac/problem/2001 题解 可以发现这个题就是 bzo ...
- BZOJ 4817 [SDOI2017]树点涂色 (LCT+线段树维护dfs序)
题目大意:略 涂色方式明显符合$LCT$里$access$操作的性质,相同颜色的节点在一条深度递增的链上 用$LCT$维护一个树上集合就好 因为它维护了树上集合,所以它别的啥都干不了了 发现树是静态的 ...
- 【bzoj4817】树点涂色 LCT+线段树+dfs序
Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色. ...
- BZOJ 4817 [Sdoi2017]树点涂色 ——LCT 线段树
同BZOJ3779. SDOI出原题,还是弱化版的. 吃枣药丸 #include <map> #include <cmath> #include <queue> # ...
- BZOJ 4817: [Sdoi2017]树点涂色(lct+线段树)
传送门 解题思路 跟重组病毒这道题很像.只是有了一个询问\(2\)的操作,然后询问\(2\)的答案其实就是\(val[x]+val[y]-2*val[lca(x,y)]+1\)(画图理解).剩下的操作 ...
随机推荐
- vim编辑器配置及常用命令
最近工作不安分, 没有了刚入行时候的锐气, 不知道什么时候开始懈怠起来, 周末在电脑旁边看新闻, 搞笑图片, 追美剧, 一坐就是一天, 很是空虚. 我需要摆脱这种状态, 正好想学习一下安卓底层, An ...
- LintCode-4.丑数 II
丑数 II 设计一个算法,找出只含素因子2,3,5 的第 n 大的数. 符合条件的数如:1, 2, 3, 4, 5, 6, 8, 9, 10, 12... 注意事项 我们可以认为1也是一个丑数 样例 ...
- veeValidate
网站 http://vee-validate.logaretm.com/index.html#about 自定义为空时候的提示 Field-specific Custom Messages You m ...
- 【bzoj3437】小P的牧场 斜率优化dp
题目描述 背景 小P是个特么喜欢玩MC的孩纸... 描述 小P在MC里有n个牧场,自西向东呈一字形排列(自西向东用1…n编号),于是他就烦恼了:为了控制这n个牧场,他需要在某些牧场上面建立控制站,每个 ...
- Python 文件对象和方法
Python文件对象和方法 1.打开和关闭文件 Python提供了必要的函数和方法进行默认情况下的文件基本操作,我们可以用file对象做大部分文件操作. open()方法 我们必须先用Python内置 ...
- FreeBSD查看网络情况
FreeBSD查看网络情况 记录一下FreeBSD下常用的统计当前系统网络连接状态的一些命令: 1)统计80端口连接数 netstat -nat|grep -i "80"|wc - ...
- C++中Set的使用
/* #include <string> // 使用 string 类时须包含这个文件 #include <iostream> // 这个就加上去吧.c++的输入和输出. us ...
- 【题解】JXOI2017颜色
一眼线段树...显然,我们可以考虑最后所留下的区间,那显然这个区间中应当不能存在任何与区间外相同的颜色.这里的转化也是很常用的,我们用 \(nxt[i]\) 表示与 \(i\) 颜色相同的下一个位置在 ...
- 【刷题】BZOJ 2744 [HEOI2012]朋友圈
Description 在很久很久以前,曾经有两个国家和睦相处,无忧无虑的生活着.一年一度的评比大会开始了,作为和平的两国,一个朋友圈数量最多的永远都是最值得他人的尊敬,所以现在就是需要你求朋友圈的最 ...
- [BZOJ3380] [USACO2004 Open]Cave Cows 1 洞穴里的牛之一
Description 很少人知道其实奶牛非常喜欢到洞穴里面去探险. 洞窟里有N(1≤N≤100)个洞室,由M(1≤M≤1000)条双向通道连接着它们.每对洞室间 至多只有一条双向通道.有K( ...