bzoj3307雨天的尾巴(权值线段树合并/DSU on tree)
题目大意:
一颗树,想要在树链上添加同一物品,问最后每个点上哪个物品最多。
解题思路:
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)的更多相关文章
- 【bzoj3307】雨天的尾巴 权值线段树合并
题目描述 N个点,形成一个树状结构.有M次发放,每次选择两个点x,y,对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. 输入 第一行数字N,M接下来 ...
- bzoj3307 雨天的尾巴 题解(线段树合并+树上差分)
Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y 对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成 所有发放后,每个点存放最多的是哪种物品. Input ...
- B20J_2733_[HNOI2012]永无乡_权值线段树合并
B20J_2733_[HNOI2012]永无乡_权值线段树合并 Description:n座岛,编号从1到n,每座岛都有自己的独一无二的重要度,按照重要度可以将这n座岛排名,名次用1到 n来表示.某些 ...
- 【bzoj1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+权值线段树合并
题目描述 求一张图的严格次小生成树的边权和,保证存在. 输入 第一行包含两个整数N 和M,表示无向图的点数与边数. 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z ...
- 【bzoj4719】[Noip2016]天天爱跑步 权值线段树合并
题目描述 给出一棵n个点的树,以及m次操作,每次操作从起点向终点以每秒一条边的速度移动(初始时刻为0),最后对于每个点询问有多少次操作在经过该点的时刻为某值. 输入 第一行有两个整数N和M .其中N代 ...
- 【bzoj2212】[Poi2011]Tree Rotations 权值线段树合并
原文地址:http://www.cnblogs.com/GXZlegend/p/6826614.html 题目描述 Byteasar the gardener is growing a rare tr ...
- luogu3224 永无乡(动态开点,权值线段树合并)
luogu3224 永无乡(动态开点,权值线段树合并) 永无乡包含 n 座岛,编号从 1 到 n ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 n 座岛排名,名次用 1 到 n 来表示.某些 ...
- 【bzoj4399】魔法少女LJJ 并查集+权值线段树合并
题目描述 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处散发着醉人的奶浆味: ...
- HDU-6704 K-th occurrence (后缀自动机father树上倍增建权值线段树合并)
layout: post title: HDU-6704 K-th occurrence (后缀自动机father树上倍增建权值线段树合并) author: "luowentaoaa&quo ...
随机推荐
- win7防火墙里开启端口的图文教程
转载于:http://www.cnblogs.com/vipsoft/archive/2012/05/02/2478847.html 开启端口:打开“控制面板”中的“Windows防火墙”,点击左侧的 ...
- js正则学习分享
http://www.cnblogs.com/rubylouvre/archive/2010/03/09/1681222.html http://www.cnblogs.com/tylerdonet/ ...
- 原生ajax实现方式
http://www.cnblogs.com/rubylouvre/archive/2013/01/08/2851051.html <!DOCTYPE html> <html lan ...
- [Chromium文档转载,第002章]Mojo C++ Bindings API
Mojo C++ Bindings API This document is a subset of the Mojo documentation. Contents Overview Getting ...
- 在Ubuntu14.04中安装Py3和切换Py2和Py3环境
前几天小编给大家分享了如何安装Ubuntu14.04系统,感兴趣的小伙伴可以戳这篇文章:手把手教你在VMware虚拟机中安装Ubuntu14.04系统.今天小编给大家分享一下在Ubuntu14.04系 ...
- CCF模拟题 字符串匹配
字符串匹配 时间限制: 1.0s 内存限制: 256.0MB 问题描述 给出一个字符串和多行文字,在这些文字中找到字符串出现的那些行.你的程序还需支持大小写敏感选项:当选项打开时,表示同一个字母的大写 ...
- [Python] Finding the most common elements in an iterable
>>> import collections >>> # Tally occurrences of words in a list >>> cnt ...
- Spring MVC : Java模板引擎 Thymeleaf (三)
以下以构造一个表单開始,解说 Thymeleaf的使用方法. 为了演示方便,还是以经典的注冊为例. 这是Thymeleaf的form的形式, <form action="#" ...
- servlet学习(1)
1.Servlet是sun公司提供的一门用于开发动态web资源的技术. 2.Servlet在web应用的位置: 3.创建Servlet的三种方式: (1)实现servlet的接口 (2)继承Gener ...
- [Java开发之路](9)对象序列化与反序列化
1. 对象序列化 当你创建对象时.仅仅要你须要.它会一直存在,可是程序终止时,不管何时它都不会继续存在.虽然这样做是很有意义的,可是在某些情况下.假设程序不执行时扔能存在而且保存其信息,那将对我们很实 ...