[BZOJ2243][SDOI2011]染色 解题报告|树链剖分
Description
给定一棵有n个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”、“222”和“1”。
请你写一个程序依次完成这m个操作。
与上一题差别不大,主要就是solve过程要根据左端点和右端点的颜色处理合并时候的情况
线段树的每个节点要记录颜色段数|最左边的颜色|最右边的颜色
同时往下传的时候标记要做好(之前那道题是单点修改所以不用考虑下传
因为这个昨晚调了一个晚上无果...果然是早上头脑清醒的多 一下子就改好了...
program bzoj2243;
const maxn=;maxm=;
var ter,next:array[-..maxm]of longint;
link,pos,deep,size,belong,v:array[-..maxn]of longint;
fa:array[-..maxn,-..]of longint;
cnt,n,m,i,j,x,y,z,t:longint;
ch:char;
tr:array[-..*maxn]of record l,r,lc,rc,cover:longint;wait:boolean;end; procedure add(x,y:longint);
begin
inc(j);ter[j]:=y;next[j]:=link[x];link[x]:=j;
inc(j);ter[j]:=x;next[j]:=link[y];link[y]:=j;
end; procedure dfs1(p:longint);
var i,j:longint;
begin
size[p]:=;
for i:= to do
begin
if deep[p]<= << i then break;
fa[p][i]:=fa[fa[p][i-]][i-];
end;
j:=link[p];
while j<> do
begin
if deep[ter[j]]= then
begin
deep[ter[j]]:=deep[p]+;
fa[ter[j]][]:=p;
dfs1(ter[j]);
inc(size[p],size[ter[j]]);
end;
j:=next[j];
end;
end; procedure dfs2(p,chain:longint);
var j,k:longint;
begin
inc(cnt);pos[p]:=cnt;belong[p]:=chain;
k:=;
j:=link[p];
while j<> do
begin
if deep[ter[j]]>deep[p] then
if size[ter[j]]>size[k] then k:=ter[j];
j:=next[j];
end;
if k= then exit;
dfs2(k,chain);
j:=link[p];
while j<> do
begin
if (deep[ter[j]]>deep[p])and(k<>ter[j]) then dfs2(ter[j],ter[j]);
j:=next[j];
end;
end; procedure build(p,l,r:longint);
var mid:longint;
begin
tr[p].l:=l;tr[p].r:=r;tr[p].cover:=;tr[p].lc:=-;tr[p].rc:=-;
if l=r then exit;
mid:=(l+r) >> ;
build(p << ,l,mid);
build(p << +,mid+,r);
end; procedure insert(p,l,r,x:longint);
var mid:longint;
begin
if (tr[p].l=l)and(tr[p].r=r) then
begin
tr[p].cover:=;tr[p].lc:=x;tr[p].rc:=x;tr[p].wait:=true;
exit;
end;
mid:=(tr[p].l+tr[p].r) >> ;
if tr[p].wait then
begin
tr[p << ].cover:=;tr[p << ].lc:=tr[p].lc;tr[p << ].rc:=tr[p].rc;
tr[p << +].cover:=;tr[p << +].lc:=tr[p].lc;tr[p << +].rc:=tr[p].rc;
tr[p].wait:=false;
tr[p << ].wait:=true;tr[p << +].wait:=true;
end;
if r<=mid then insert(p << ,l,r,x) else
if l>mid then insert(p << +,l,r,x) else
begin
insert(p << ,l,mid,x);
insert(p << +,mid+,r,x);
end;
tr[p].cover:=tr[p << ].cover+tr[p << +].cover;
if tr[p << ].rc=tr[p << +].lc then dec(tr[p].cover);
tr[p].lc:=tr[p << ].lc;tr[p].rc:=tr[p << +].rc;
end; function lca(x,y:longint):longint;
var tem,i:longint;
begin
if deep[x]<deep[y] then
begin
tem:=x;x:=y;y:=tem;
end;
if deep[x]>deep[y] then
begin
i:=trunc(ln(deep[x]-deep[y])/ln());
while deep[x]<>deep[y] do
begin
while (deep[x]-deep[y]>= << i) do x:=fa[x][i];
dec(i);
end;
end;
if x=y then exit(x);
i:=trunc(ln(n)/ln());
while fa[x][]<>fa[y][] do
begin
while (fa[x][i]<>fa[y][i]) do
begin
x:=fa[x,i];y:=fa[y][i];
end;
dec(i);
end;
exit(fa[x,]);
end; procedure query(p,l,r:longint;var lc,rc,ave:longint);
var mid,ll,rr,x,tem:longint;
begin
if (tr[p].l=l)and(tr[p].r=r) then
begin
lc:=tr[p].lc;rc:=tr[p].rc;
ave:=tr[p].cover;
exit;
end;
mid:=(tr[p].l+tr[p].r) >> ;
if tr[p].wait then
begin
tr[p << ].cover:=;tr[p << ].lc:=tr[p].lc;tr[p << ].rc:=tr[p].rc;
tr[p << +].cover:=;tr[p << +].lc:=tr[p].lc;tr[p << +].rc:=tr[p].rc;
tr[p].wait:=false;
tr[p << ].wait:=true;tr[p << +].wait:=true;
end;
if r<=mid then query(p << ,l,r,lc,rc,ave) else
if l>mid then query(p << +,l,r,lc,rc,ave) else
begin
query(p << ,l,mid,lc,ll,ave);
query(p << +,mid+,r,rr,rc,tem);
inc(ave,tem);
if ll=rr then dec(ave);
end;
end; function solve(x,y:longint):longint;
var r,sum,lc,rc,tem:longint;
begin
r:=-;sum:=;
while belong[x]<>belong[y] do
begin
query(,pos[belong[x]],pos[x],lc,rc,tem);
inc(sum,tem);
if rc=r then dec(sum);
r:=lc;
x:=fa[belong[x]][];
end;
query(,pos[y],pos[x],lc,rc,tem);
inc(sum,tem);
if rc=r then dec(sum);
exit(sum);
end; procedure mend(x,y,z:longint);
begin
while belong[x]<>belong[y] do
begin
insert(,pos[belong[x]],pos[x],z);
x:=fa[belong[x]][];
end;
insert(,pos[y],pos[x],z);
end; begin
readln(n,m);
for i:= to n do read(v[i]);readln;
j:=;
for i:= to n- do
begin
readln(x,y);
add(x,y);
end;
deep[]:=;
dfs1();
cnt:=;dfs2(,);
build(,,n);
for i:= to n do insert(,pos[i],pos[i],v[i]);
for i:= to m do
begin
read(ch);
if ch='C' then
begin
readln(x,y,z);
t:=lca(x,y);
mend(x,t,z);mend(y,t,z);
end else
begin
readln(x,y);
z:=lca(x,y);
writeln(solve(x,z)+solve(y,z)-);
end;
end;
end.
[UPD.5.11]今天复习树链剖分,挑了这道题写了写。一个地方WA检查了三遍对拍了一个多小时才找出来...
就是在判断链之间答案合并的时候是从右往左的而不是从左往右...习惯性的写法干脆检查的时候都忽略了这一点...
另外这次用C++大概150行左右..感觉还不错
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#define maxn 1000010
#define maxm 2000010
struct node{
int l,r,lc,rc,count,wait;
}tr[maxn*];
int next[maxm],ter[maxm],link[maxn],pos[maxn],belong[maxn],dep[maxn];
int fa[maxn][],sz[maxn],n,m,e,v[maxn],cnt;
char ch[];
void swap(int &x,int &y){
int tmp=x;x=y;y=tmp;
}
void add(int x,int y){
ter[++e]=y;next[e]=link[x];link[x]=e;
ter[++e]=x;next[e]=link[y];link[y]=e;
}
void dfs1(int p){
sz[p]=;
for (int i=;i<=&&dep[p]>(<<i);i++) fa[p][i]=fa[fa[p][i-]][i-];
for (int i=link[p];i;i=next[i]) if (dep[ter[i]]==){
dep[ter[i]]=dep[p]+;
fa[ter[i]][]=p;
dfs1(ter[i]);
sz[p]+=sz[ter[i]];
}
}
void dfs2(int p,int chain){
pos[p]=++cnt;belong[p]=chain;
int k=;
for (int i=link[p];i;i=next[i]) if (dep[ter[i]]==dep[p]+&&sz[ter[i]]>sz[k]) k=ter[i];
if (k==) return;
dfs2(k,chain);
for (int i=link[p];i;i=next[i]) if (dep[ter[i]]==dep[p]+&&ter[i]!=k) dfs2(ter[i],ter[i]);
}
void update(int p){
tr[p].lc=tr[p<<].lc;
tr[p].rc=tr[(p<<)+].rc;
tr[p].count=tr[p<<].count+tr[(p<<)+].count;
if (tr[p<<].rc==tr[(p<<)+].lc) tr[p].count--;
}
void push(int p){
if (tr[p].wait==-) return;
int u=p<<,w=(p<<)+,col=tr[p].wait;
tr[u].lc=tr[u].rc=tr[u].wait=col;tr[u].count=;
tr[w].lc=tr[w].rc=tr[w].wait=col;tr[w].count=;
tr[p].wait=-;
}
void build(int p,int l,int r){
tr[p].l=l;tr[p].r=r;tr[p].wait=-;
if (l==r) return;
int mid=(l+r) >> ;
build(p<<,l,mid);build((p<<)+,mid+,r);
}
int lca(int x,int y){
if (dep[x]<dep[y]) swap(x,y);
int i;
if (dep[x]>dep[y]){
i=(int)(log(dep[x]-dep[y])/log());
while (dep[x]>dep[y]){
while (dep[x]-dep[y]>=<<i) x=fa[x][i];
i--;
}
}
if (x==y) return x;
i=(int)(log(n)/log());
while (fa[x][]!=fa[y][]){
while (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
i--;
}
return fa[x][];
}
void insert(int p,int l,int r,int col){
if (tr[p].l==l&&tr[p].r==r){
tr[p].lc=col;tr[p].rc=col;tr[p].wait=col;tr[p].count=;
return;
}
push(p);
int mid=(tr[p].l+tr[p].r)>>;
if (r<=mid) insert(p<<,l,r,col);else
if (l>mid) insert((p<<)+,l,r,col);else{
insert(p<<,l,mid,col);insert((p<<)+,mid+,r,col);
}
update(p);
}
void query(int p,int l,int r,int &lc,int &rc,int &ans){
if (tr[p].l==l&&tr[p].r==r){
lc=tr[p].lc;rc=tr[p].rc;ans=tr[p].count;
return;
}
push(p);
int mid=(tr[p].l+tr[p].r)>>;
if (r<=mid) query(p<<,l,r,lc,rc,ans);else
if (l>mid) query((p<<)+,l,r,lc,rc,ans);else
{
int ll,rr,ans1;
query(p<<,l,mid,lc,rr,ans);
query((p<<)+,mid+,r,ll,rc,ans1);
ans+=ans1;if (ll==rr) ans--;
}
update(p);
}
void change(int x,int y,int col){
while (belong[x]!=belong[y]){
insert(,pos[belong[x]],pos[x],col);
x=fa[belong[x]][];
}
insert(,pos[y],pos[x],col);
}
int solve(int x,int y){
int sum=,r=-,ll,rr,tmp;
while (belong[x]!=belong[y]){
query(,pos[belong[x]],pos[x],ll,rr,tmp);
x=fa[belong[x]][];
sum+=tmp;if (rr==r) sum--;
r=ll;
}
query(,pos[y],pos[x],ll,rr,tmp);
sum+=tmp;if (rr==r) sum--;
return sum;
}
int main(){
scanf("%d%d",&n,&m);
for (int i=;i<=n;i++) scanf("%d",&v[i]);
e=;
int x,y,col,t;
for (int i=;i<=n-;i++) scanf("%d%d",&x,&y),add(x,y);
dep[]=;dfs1();
cnt=;dfs2(,);
build(,,n);
for (int i=;i<=n;i++) insert(,pos[i],pos[i],v[i]);
for (int i=;i<=m;i++){
scanf("%s",ch);
if (ch[]=='C'){
scanf("%d%d%d",&x,&y,&col);
t=lca(x,y);
change(x,t,col);change(y,t,col);
}else{
scanf("%d%d",&x,&y);
t=lca(x,y);
printf("%d\n",solve(x,t)+solve(y,t)-);
}
}
return ;
}
[BZOJ2243][SDOI2011]染色 解题报告|树链剖分的更多相关文章
- [POJ3237]Tree解题报告|树链剖分|边剖
关于边剖 之前做的大多是点剖,其实转换到边剖非常简单. 我的做法是每个点的点权记录其到父亲节点的边的边权. 只要solve的时候不要把最上面的点记录在内就可以了. Tree Description Y ...
- [BZOJ1036][ZJOI2008]树的统计Count 解题报告|树链剖分
树链剖分 简单来说就是数据结构在树上的应用.常用的为线段树splay等.(可现在splay还不会敲囧) 重链剖分: 将树上的边分成轻链和重链. 重边为每个节点到它子树最大的儿子的边,其余为轻边. 设( ...
- [jzoj 3175] 数树数 解题报告 (树链剖分)
interlinkage: https://jzoj.net/senior/#main/show/3175 description: 给定一棵N 个节点的树,标号从1~N.每个点有一个权值.要求维护两 ...
- [BZOJ1984]月下“毛景树”解题报告|树链剖分
Description 毛毛虫经过及时的变形,最终逃过的一劫,离开了菜妈的菜园. 毛毛虫经过千山万水,历尽千辛万苦,最后来到了小小的绍兴一中的校园里.爬啊爬~爬啊爬~~毛毛虫爬到了一颗小小的“毛景树” ...
- P2486 [SDOI2011]染色 区间合并+树链剖分(加深对线段树的理解)
#include<bits/stdc++.h> using namespace std; ; struct node{ int l,r,cnt,lazy; node(,,,):l(l1), ...
- P2486 [SDOI2011]染色 区间合并+树链剖分(加深对线段树的理解)
#include<bits/stdc++.h> using namespace std; ; struct node{ int l,r,cnt,lazy; node(,,,):l(l1), ...
- 洛谷 P2486 [SDOI2011]染色/bzoj 2243: [SDOI2011]染色 解题报告
[SDOI2011]染色 题目描述 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同 ...
- 【bzoj1959】[Ahoi2005]LANE 航线规划 树链剖分+线段树
题目描述 对Samuel星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了Samuel星球所在的星系——一个巨大的由千百万星球构成的Samuel星系. 星际空间站的Samuel II巨型计算 ...
- 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树
[BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...
随机推荐
- 美年健康股票成交量和K线关系
看下美年健康的股票,这次主要是研究下成交量和K线的关系,以最后5天为例子,股票下跌成交量降低,说明抛压很小,在最后3天,价格突破的时候,成交量是平时的两倍,说明有机构买入, 业绩部分还可以,全民健身是 ...
- PS作业
- 【jQuery】 js 对象
[jQuery] js 对象 一. 创建对象的三种方式 <script> var v1 = new Object(); v1.name = "name1"; v1.a ...
- VS2013生产过程问题及解决
TRK0002错误 现象:编译器.链接器交替报错,不能正常生成 环境:Win8.1 + VS2013 + 百度杀毒 解决:退出百度杀毒,重启VS,再进行生成 修订:发现问题依旧,经过多次试验,发现与杀 ...
- 自动化测试学习之路--java 数组
数组的定义与为数组元素分配空间和赋值是分开进行的,称为动态初始化. 在数组定义的同时就为数组元素分配空间并赋值,称为静态初始化. 一维数组举例: //动态初始化 int[] intArr; intAr ...
- 《python核心编程第二版》第3章习题
3-1. 标识符.为什么 Python 中不需要变量名和变量类型声明? 答: 对象的类型和内存占用都是运行时确定的. 尽管代码被编译成字节码,Python 仍然是一种解释 型语言. 在创建也就是赋值时 ...
- JavaScript调试中Console命令
JS调试中,用console.log 感觉比 alert 好用,不用弹出窗口,还要关闭.除了console.log()其他命令没怎么用过,先在这里记一下,用到时在看看 一.显示信息的命令 consol ...
- Mybatis学习系列(五)关联查询
前面几节的示例基本都是一些单表查询,实际项目中,经常用到关联表的查询,比如一对一,一对多等情况.在Java实体对象中,一对一和一对多可是使用包装对象解决,属性使用List或者Set来实现,在mybat ...
- css实现div一直旋转
看到音乐播放器播放界面的唱片一直旋转,突然想到在网页中的一直旋转效果,所有特地查了一下分享出来 这是一个静态的div,然后把它旋转动起来.效果请看右上角的音乐播放按键一样. 代码如下: <htm ...
- 【EasyNetQ】- 简介
EasyNetQ是一个简单易用的,稳定的的RabbitMQ .NET API . 如果您只想尽快启动并运行,请转到“ 快速开始”指南. EasyNetQ的目标是提供一个库,使得在.NET中使用Rabb ...