题目大意:

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

解题思路:

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. 百度LBS开放平台个性化地图 制作一款独一无二的地图

    百度LBS开放平台个性化地图  制作一款独一无二的地图 天天用百度地图的亲们是否已不再满足仅仅看例如以下的地图样式了呢? 默认百度地图样式 是否特别渴望看特别不一样的地图呢.如带京城81号气息的午夜蓝 ...

  2. IDEA创建maven项目之后无法编写java类

    在创建Maven web项目之后无法再java文件夹下面创建java类,这里我可以教一下大家 选择你的文件夹,鼠标点击右键,出现下图所显示的,你可以按照下图所显示的步骤进行操作

  3. idea里新建maven项目时,在new module页面,一直显示loading archetype list...

    不知道什么时候开始,在idea里新建maven项目时,在new module页面,一直显示loading archetype list....,导致一直没办法新建.后来我以为是防火墙问题,各种设置还是 ...

  4. HDU2665 kth number 线段树做法

    题意:求区间第k小 思路: 线段树 每个节点上保存 当前区间已经排序好的序列 (归并一下就好了嘛 复杂度 O(l)的) 这样建树的时空复杂度都是 O(nlogn)的 对于 每次询问 二分一个答案 在树 ...

  5. 如何在Centos官网下载所需版本的Centos——靠谱的Centos下载教程

    很多小伙伴不知道对应版本的Centos怎么下载,最近小编整理了一份Centos详细的下载教程,希望小伙伴们不在为下不到对应版本的Centos而苦恼. 1.进入Centos官网:https://www. ...

  6. nginx编译安装选项说明

    configure 脚本确定系统所具有一些特性,特别是 nginx 用来处理连接的方法.然后,它创建 Makefile 文件. configure 支持下面的选项: –prefix= <path ...

  7. CentOS下安装jdk1.8.0_181

    我安装的为 jdk1.8.0_181 1.检查是否存在open jdk,不存在直接跳到第 5 步 java -version 查看当前系统自带的open jdk版本信息 2.查看包含java字符串的文 ...

  8. php验证邮箱,手机号是否正确

    function is_valid_email($email)//判断是不是邮箱的函数{    return preg_match('/^[a-zA-Z0-9._%-]+@([a-zA-Z0-9.-] ...

  9. SpringBoot常用注解的介绍及使用 - 转载

    常用注解 @springBootApplication 系统启动类注解,此注解是个组合注解,包括了:@SpringBootConfiguration,@EnableAutoConfiguration, ...

  10. UML类图中的几种关系总结

           UML类图,描写叙述对象和类之间相互关系的方式包含:依赖(Dependency).关联(Association).聚合(Aggregation).组合(Composition).泛化(G ...