题目大意:

一颗树,想要在树链上添加同一物品,问最后每个点上哪个物品最多。

解题思路:

1.线段树合并

假如说物品数量少到可以暴力添加,且树点极少,我们怎么做。

首先在一个树节点上标记出哪些物品有多少,寻找道路上的节点暴力添加。

而如果节点比较多,我们使用树上差分u+1,v+1,lca-1,fa[lca]-1向上求和就得到了答案

而颜色较多呢,和刚才唯一不同就在于向上求和时不要用数组,用线段树合并就好了(记录线段树区间的最大值与最大位置)

废点的回收是非常实用的^_^

时间复杂度O(nlogn)

代码:

 #include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lll tr[spc].ls
#define rrr tr[spc].rs
const int maxin=;
struct pnt{
int fa[];
int hd;
int gd;
int dp;
int wgt;
int ans;
int prp;
int root;
}p[];
struct ent{
int twd;
int lst;
}e[];
struct ljt{
int twd;
int lst;
int vls;
}el[];
struct trnt{
int ls;
int rs;
int val;
int mpl;
}tr[],org;
int bin[];
int top;
int siz;
int cnt1;
int cnt2;
int n,m;
void adde(int f,int t)
{
cnt1++;
e[cnt1].twd=t;
e[cnt1].lst=p[f].hd;
p[f].hd=cnt1;
return ;
}
void addc(int pt,int c,int v)
{
cnt2++;
el[cnt2].twd=c;
el[cnt2].lst=p[pt].gd;
el[cnt2].vls=v;
p[pt].gd=cnt2;
return ;
}
namespace Sgt{
void P_delete(int spc)
{
bin[++top]=spc;
tr[spc]=org;
return ;
}
void P_destory(int spc1,int spc2)
{
P_delete(spc1);
P_delete(spc2);
return ;
}
int P_new(void)
{
if(top)
return bin[top--];
return ++siz;
}
void pushup(int spc)
{
if(tr[lll].val>=tr[rrr].val)
tr[spc].val=tr[lll].val,tr[spc].mpl=tr[lll].mpl;
else
tr[spc].val=tr[rrr].val,tr[spc].mpl=tr[rrr].mpl;
return ;
}
int P_merge(int spc1,int spc2,int l,int r)
{
if(!spc1||!spc2)
return spc1+spc2;
int spc=P_new();
if(l==r)
{
tr[spc].val=tr[spc1].val+tr[spc2].val;
tr[spc].mpl=l;
P_destory(spc1,spc2);
return spc;
}
int mid=(l+r)>>;
tr[spc].ls=P_merge(tr[spc1].ls,tr[spc2].ls,l,mid);
tr[spc].rs=P_merge(tr[spc1].rs,tr[spc2].rs,mid+,r);
pushup(spc);
P_destory(spc1,spc2);
return spc;
}
void Update(int l,int r,int &spc,int pos,int v)
{
if(!spc)
spc=P_new();
if(l==r)
{
tr[spc].val+=v;
tr[spc].mpl=(tr[spc].val)?l:;
return ;
}
int mid=(l+r)>>;
if(pos<=mid)
Update(l,mid,tr[spc].ls,pos,v);
else
Update(mid+,r,tr[spc].rs,pos,v);
pushup(spc);
if(!tr[spc].val)
tr[spc].mpl=;
return ;
}
}
void Basic_dfs(int x,int f)
{
p[x].dp=p[f].dp+;
p[x].fa[]=f;
for(int i=;i<=;i++)
p[x].fa[i]=p[p[x].fa[i-]].fa[i-];
p[x].wgt=;
for(int i=p[x].hd;i;i=e[i].lst)
{
int to=e[i].twd;
if(to==f)
continue;
Basic_dfs(to,x);
p[x].wgt+=p[to].wgt;
}
return ;
}
void Ans_dfs(int x,int f)
{
for(int i=p[x].hd;i;i=e[i].lst)
{
int to=e[i].twd;
if(to==f)
continue;
Ans_dfs(to,x);
}
for(int i=p[x].gd;i;i=el[i].lst)
{
int clr=el[i].twd;
Sgt::Update(,maxin,p[x].root,clr,el[i].vls);
}
p[x].ans=tr[p[x].root].mpl;
p[f].root=Sgt::P_merge(p[f].root,p[x].root,,maxin);
}
int Lca(int x,int y)
{
if(p[x].dp<p[y].dp)
std::swap(x,y);
for(int i=;i>=;i--)
if(p[p[x].fa[i]].dp>=p[y].dp)
x=p[x].fa[i];
if(x==y)
return x;
for(int i=;i>=;i--)
if(p[x].fa[i]!=p[y].fa[i])
x=p[x].fa[i],y=p[y].fa[i];
return p[x].fa[];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
adde(a,b);
adde(b,a);
}
Basic_dfs(,);
for(int i=;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
addc(x,z,);
addc(y,z,);
int lca=Lca(x,y);
addc(lca,z,-);
if(lca!=)
addc(p[lca].fa[],z,-);
}
Ans_dfs(,);
for(int i=;i<=n;i++)
printf("%d\n",p[i].ans);
return ;
}

2.DSU on tree

如同经典的DSU on tree,重链剖分,对轻儿子暴力修改,只不过使用权值线段树而不是桶,时间复杂度O(nlog2n),慢了点常数也大,但数据105还是没有问题的

代码:

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define lll spc<<1
#define rrr spc<<1|1
const int maxc=;
struct pnt{
int hd;
int gd;
int fa;
int tp;
int dp;
int wgt;
int mxs;
int ans;
}p[];
struct ent{
int twd;
int lst;
}e[];
struct clr{
int twd;
int lst;
int vls;
}c[];
struct trnt{
int val;
int maxp;
}tr[],stdtr;
int n,m;
int cnt;
int clt;
void ade(int f,int t)
{
cnt++;
e[cnt].twd=t;
e[cnt].lst=p[f].hd;
p[f].hd=cnt;
}
void adc(int f,int t,int v)
{
clt++;
c[clt].twd=t;
c[clt].lst=p[f].gd;
c[clt].vls=v;
p[f].gd=clt;
}
void pushup(int spc)
{
if(tr[lll].val>=tr[rrr].val)
tr[spc]=tr[lll];
else
tr[spc]=tr[rrr];
}
void update(int l,int r,int spc,int pos,int v)
{
if(l==r)
{
tr[spc].val+=v;
tr[spc].maxp=pos;
if(!tr[spc].val)
tr[spc].maxp=;
return ;
}
int mid=(l+r)>>;
if(pos<=mid)
update(l,mid,lll,pos,v);
else
update(mid+,r,rrr,pos,v);
pushup(spc);
return ;
}
void Destroy(int l,int r,int spc)
{
tr[spc]=stdtr;
if(l==r)
return ;
int mid=(l+r)>>;
Destroy(l,mid,lll);
Destroy(mid+,r,rrr);
return ;
}
int Lca(int x,int y)
{
while(p[x].tp!=p[y].tp)
{
if(p[p[x].tp].dp<p[p[y].tp].dp)
std::swap(x,y);
x=p[p[x].tp].fa;
}
if(p[x].dp>p[y].dp)
std::swap(x,y);
return x;
}
void Basic_dfs(int x,int f)
{
int maxs=-;
p[x].wgt=;
p[x].dp=p[f].dp+;
p[x].fa=f;
for(int i=p[x].hd;i;i=e[i].lst)
{
int to=e[i].twd;
if(to==f)
continue;
Basic_dfs(to,x);
p[x].wgt+=p[to].wgt;
if(p[to].wgt>maxs)
{
maxs=p[to].wgt;
p[x].mxs=to;
}
}
return ;
}
void Sec_dfs(int x,int f,int top)
{
if(!x)
return ;
p[x].tp=top;
Sec_dfs(p[x].mxs,x,top);
for(int i=p[x].hd;i;i=e[i].lst)
{
int to=e[i].twd;
if(!p[to].tp)
Sec_dfs(to,x,to);
} }
void Add_dfs(int x,int f)
{
for(int i=p[x].gd;i;i=c[i].lst)
update(,maxc,,c[i].twd,c[i].vls);
for(int i=p[x].hd;i;i=e[i].lst)
if(e[i].twd!=f)
Add_dfs(e[i].twd,x);
}
void Des_dfs(int x,int f)
{
for(int i=p[x].gd;i;i=c[i].lst)
update(,maxc,,c[i].twd,-c[i].vls);
for(int i=p[x].hd;i;i=e[i].lst)
if(e[i].twd!=f)
Des_dfs(e[i].twd,x);
}
void DSU_dfs(int x,int f,bool hv)
{
if(!x)
return ;
for(int i=p[x].hd;i;i=e[i].lst)
{
int to=e[i].twd;
if(to==f||to==p[x].mxs)
continue;
DSU_dfs(to,x,false);
}
DSU_dfs(p[x].mxs,x,true);
for(int i=p[x].hd;i;i=e[i].lst)
{
int to=e[i].twd;
if(to==f||to==p[x].mxs)
continue;
Add_dfs(to,x);
}
for(int i=p[x].gd;i;i=c[i].lst)
update(,maxc,,c[i].twd,c[i].vls);
p[x].ans=tr[].maxp;
if(!hv)
Des_dfs(x,f);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
ade(a,b);
ade(b,a);
}
Basic_dfs(,);
Sec_dfs(,,);
for(int i=;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
int l=Lca(x,y);
adc(x,z,);
adc(y,z,);
adc(l,z,-);
if(l!=)
adc(p[l].fa,z,-);
}
DSU_dfs(,,);
for(int i=;i<=n;i++)
{
printf("%d\n",p[i].ans);
}
return ;
}

bzoj3307雨天的尾巴(权值线段树合并/DSU on tree)的更多相关文章

  1. 【bzoj3307】雨天的尾巴 权值线段树合并

    题目描述 N个点,形成一个树状结构.有M次发放,每次选择两个点x,y,对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. 输入 第一行数字N,M接下来 ...

  2. bzoj3307 雨天的尾巴 题解(线段树合并+树上差分)

    Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y 对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成 所有发放后,每个点存放最多的是哪种物品. Input ...

  3. B20J_2733_[HNOI2012]永无乡_权值线段树合并

    B20J_2733_[HNOI2012]永无乡_权值线段树合并 Description:n座岛,编号从1到n,每座岛都有自己的独一无二的重要度,按照重要度可以将这n座岛排名,名次用1到 n来表示.某些 ...

  4. 【bzoj1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+权值线段树合并

    题目描述 求一张图的严格次小生成树的边权和,保证存在. 输入 第一行包含两个整数N 和M,表示无向图的点数与边数. 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z ...

  5. 【bzoj4719】[Noip2016]天天爱跑步 权值线段树合并

    题目描述 给出一棵n个点的树,以及m次操作,每次操作从起点向终点以每秒一条边的速度移动(初始时刻为0),最后对于每个点询问有多少次操作在经过该点的时刻为某值. 输入 第一行有两个整数N和M .其中N代 ...

  6. 【bzoj2212】[Poi2011]Tree Rotations 权值线段树合并

    原文地址:http://www.cnblogs.com/GXZlegend/p/6826614.html 题目描述 Byteasar the gardener is growing a rare tr ...

  7. luogu3224 永无乡(动态开点,权值线段树合并)

    luogu3224 永无乡(动态开点,权值线段树合并) 永无乡包含 n 座岛,编号从 1 到 n ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 n 座岛排名,名次用 1 到 n 来表示.某些 ...

  8. 【bzoj4399】魔法少女LJJ 并查集+权值线段树合并

    题目描述 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处散发着醉人的奶浆味: ...

  9. HDU-6704 K-th occurrence (后缀自动机father树上倍增建权值线段树合并)

    layout: post title: HDU-6704 K-th occurrence (后缀自动机father树上倍增建权值线段树合并) author: "luowentaoaa&quo ...

随机推荐

  1. 利用css3的多背景图属性实现幻灯片切换效果

    css3里关于背景的属性增加了可以添加多背景图的特性,例如: .box{background: url(img/1.png),url(img/2.png),url(img/3.png);} 这段css ...

  2. kafka查询topic属性含义

    第一行,列出了topic的名称,分区数(PartitionCount),副本数(ReplicationFactor)以及其他的配置(Config.s) Leader:1 表示为做为读写的broker的 ...

  3. Python(七) 高级部分:面向对象

    一.类的定义 # 面向对象 #有意义的面向对象代码 # 类 = 面向对象 # 类.对象 #实例化 # 类最基本的作用:封装 class Student(): name = '' age = 0 def ...

  4. idea+maven+springmvc

    黑了Java这么多年, 今天为Java写一篇文章吧. 这篇文章主要是想帮助那些刚接触到Java, 同时想从事Java WEB GUI开发的人. 对我而言, 我很早就有想尝试用Java写WEB的想法, ...

  5. 使用PyV8模块破解网站加密cookie

    PyV8是Chromium中内嵌的javascript引擎,号称跑的最快.PyV8是用Python在V8的外部API包装了一个python壳,这样便可以使python可以直接与javascript操作 ...

  6. 织梦DedeCMS判断简略标题为空时则显示完整标题

    使用织梦DedeCMS系统程序开发网站中,我们会遇到很多因网页版面设计限定的宽度,使文章标题需要进行字数限制,通常做法是在a标签中加入一个title属性,让鼠标放上去的时候显示完整标题.但是标题被剪裁 ...

  7. js编码方式详解

    escape.encodeURI 和encodeURIComponent 的区别 escape(), encodeURI()和encodeURIComponent()是在Javascript中用于编码 ...

  8. intellij—idea14 注冊机

    package com.qunar.fresh; import java.math.BigInteger; import java.util.Date; import java.util.Random ...

  9. a标签中的javascript:;是什么

    a标签中的javascript:;是什么 一.问题 <a>标签中href="javascript:;"表示什么意思?? <a id="jsPswEdit ...

  10. 谈谈iframe的优缺点

    iframe是一种框架,也是一种很常见的网页嵌入方式,零度今天给大家分析分析它的优缺点. iframe的优点: 1.iframe能够原封不动的把嵌入的网页展现出来. 2.如果有多个网页引用iframe ...