题目大意:

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

解题思路:

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. Android 五大存储方式具体解释

    SharedPreferences与Editor SharedPreferences保存的数据仅仅要是类似于配置信息格式的数据.因此它保存的数据主要是简单的key-value对形式.以下关系图 上图全 ...

  2. 三星N900(note3)刷机包 颓废N0.8.1 修复已知BUG 集成谷歌服务

    ROM介绍 8.1更新信息:攻克了来电后点击HOME出现SECPHONE已经停止的问题 去掉了桌面隐藏信息的选项,官方最新底包暂不支持这功能 增加了网友们须要验证的谷歌服务(不须要的同学同步什么的都关 ...

  3. 利用Eventlog Analyzer分析日志

    利用EventlogAnalyzer分析日志 ManageEngineEventLogAnalyzer是一个基于Web技术.实时的事件监控管理解决方案,能够提高企业网络安全.减少工作站和服务器的宕机事 ...

  4. ivms4200 远程桌面访问测试过程及问题汇总

    17.11.4 测试存储服务器配置后能否自动录像确认 10:34   4200客户端关闭 10:40  打开4200客户端软件 10:51   关机 10:56  开机,有提示出现,“防火墙阻止... ...

  5. Linux 如何重新划分Swap交换分区

    SWAP分区是LINUX暂时存储数据的交换分区,它主要是把主内存上暂时不用得数据存起来,在需要的时候再调进内存内,且作为SWAP使用的分区不用指定“MoutPoint”(载入点)它至少要等于系统上实际 ...

  6. HTML中常用的部分标签(一)

    [常见的块级标签] 标题标签<h1></h1>...<h6></h6> 水平线<hr/> 段落<p></p> 换行& ...

  7. wordcontent小结

    gitee地址: https://gitee.com/yzpdegit/test 问题描述: 计算一个文件中所包含的单词数,字符个数,行数 需求分析: WordCount的需求可以概括为:对程序设计语 ...

  8. [Python] Understand List Comprehensions in Python

    List comprehensions provide a concise way to create new lists, where each item is the result of an o ...

  9. CSS 类、伪类和伪元素差别具体解释

    CSS中的类(class)是为了方便过滤(即选择)元素,以给这类元素加入样式,class是定义在HTML文档树中的. 可是这在一些情况下是不够用的,比方用户的交互动作(悬停.激活等)会导致元素状态发生 ...

  10. nagios,zabbix对照

    nagios/zabbix对照: nagios核心功能是监控报警.是一个轻量化的监控系统. 假设须要图标显示,须要添加图标显示插件(如pnp4nagios): 假设须要存入数据库,须要对应的插件(ND ...