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 ...
随机推荐
- Codeforces Round #313 C. Gerald's Hexagon(放三角形)
C. Gerald's Hexagon time limit per test 2 seconds memory limit per test 256 megabytes input standard ...
- ztree中依据后台中传过来的node的id,将这个node的复选框置为不可用
var treeObj = $.fn.zTree.getZTreeObj("treeDemo");//树对象 var node = treeObj.getNodeByParam(& ...
- 在SSM框架中我设置拦截器filter不能通过注解获取到实现类
我在用注解注入实现类的时候,出现了这样的错误:如以下截图: 这个地方报出的错误是说明我的一个接口类型的类没有获取到,后来我就想要是我的实现类没有获取到那么我就直接new一个实现类然后再进行调用就会出现 ...
- QThread 爬坑之旅(三种办法解决QObject: Cannot create children for a parent that is in a different thread)
Cannot create children for a parent that is in a different thread. 在Qt的官方文档,大家知道有两种方式使用QThread. You ...
- 在eclipse中公布maven的多模块web项目到tomcat上及单步debug模块jar
1.在eclipse中公布maven的多模块web项目到tomcat eclipse和maven及tomcat的部署略去,还有maven的基础知识和使用在此处略去. 依照例如以下的步骤操作: 将lib ...
- matlab 常用机器学习算法的实现
1. KNN 分类 load fisheriris X = meas; Y = species; % 3 分类问题 % 通过训练集进行训练 Mdl = fitcknn(X, Y, 'NumNeighb ...
- VB.NET中文双引号的处理方法
相信朋友们也都碰到了这样的问题了,VS的IDE会不分青红皂白的把中文双引号变成英文的双引号,当然可以通过关闭自动重排功能来回避这个问题,但不是一个好的解决办法,以下这个方式不错: 如果在实际的使用中我 ...
- BZOJ 2836 树链剖分+线段树
思路: 链剖+线段树裸题 重链的标号就是DFS序 所以查子树的时候每回就 query(change[x],change[x]+size[x]-1) 就好了 剩下的应该都会吧.. //By Sirius ...
- 打印机共享 : 客户端 连接服务器打印机时提示"无法连接到打印机“
1.就是重启一下服务器端的Print Spooler服务就行了,这么简单! 2.修改打印机的共享名 操作无法完成(错误0x00000709).再次检查打印机名称,并确保打印机已连接到网络.(xp系统本 ...
- react-native signatures do not match the previously installed version;
原因:手机上已经安装过打包后的apk应用,与真机调试无法共存. 解决办法:删除手机上已经安装过的apk应用.