【GDOI2014模拟】雨天的尾巴
题目
深绘里一直很讨厌雨天。
灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。
虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连
根拔起,以及田地里的粮食被弄得一片狼藉。
无奈的深绘里和村民们只好等待救济粮来维生。
不过救济粮的发放方式很特别。
首先村落里的一共有n 座房屋,并形成一个树状结构。然后救济粮分m 次发放,每次选择
两个房屋(x,y),然后对于x 到y 的路径上(含x 和y) 每座房子里发放一袋z 类型的救济粮。
然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。
分析
相信大多数人都跟我一样,看到这道题,果断认为是树链剖分。
其实不然(我1.5h把熟练剖分打出来,以为能过50%。结果打错了,20%。然后。。。打暴力的人都是50%,#%……&*)。
我们注意到z<=10^9,那么的数据范围。
但是m<=100000,所以就先做个离散化。
。。。
接着用到个很神奇的东西,叫线段树合并。
对于树上的每一个节点开一棵线段树,记录该节点的每个z的数量以及某一段z的最大值。注意要动态开节点,否则会爆空间。
发现,如果要修改从x到y的路径,其实就是将x的z值加一,y的z值加一,lca(x,y)的z值减一以及fa[lca(x,y)]的z值减一(why?因为当线段树合并后从x到lca(x,y)的路径上和从y到lca(x,y)的路径上的每个节点的z值都加一,但lca(x,y)这个节点重复加了,那么就减掉。不过剩余的z值还会继续上传,所以在fa[lca(x,y)]**就把上传的z值减去)
合并操作
int mesh(int x,int y,int l,int r)
{
if(l==r)
{
tree[x].v+=tree[y].v;
return 0;
}
int mid=(l+r)/2;
if(tree[y].l)
{
if(!tree[x].l)
tree[x].l=tree[y].l;
else
mesh(tree[x].l,tree[y].l,l,mid);
}
if(tree[y].r)
{
if(!tree[x].r)
tree[x].r=tree[y].r;
else
mesh(tree[x].r,tree[y].r,mid+1,r);
}
tree[x].v=max(tree[tree[x].l].v,tree[tree[x].r].v);
}
当然,当所有的修改操作做完后才线段树合并,否则会超时。
#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
const int maxlongint=2147483647;
using namespace std;
struct trees
{
int l,r,v;
}tree[8000000];
struct read
{
int x,y,z;
}re[110000];
int next[201000],last[201000],to[201000],po,tot,n,m,g[200000][20],t[200000],deep[200000],f[200000],sum,ans[2000000];
bool cmp(read x,read y)
{
return x.z<y.z;
}
int bj(int x,int y)
{
next[++tot]=last[x];
last[x]=tot;
to[tot]=y;
}
int dg(int x)
{
for(int i=last[x];i;i=next[i])
{
int j=to[i];
if(j!=g[x][0])
{
deep[j]=deep[x]+1;
g[j][0]=x;
dg(j);
}
}
}
int prelca()
{
for(int j=1;j<=log2(n);j++)
{
for(int i=1;i<=n;i++)
{
g[i][j]=g[g[i][j-1]][j-1];
}
}
}
int lca(int x,int y)
{
if(deep[x]>deep[y])
{
x=x^y;
y=x^y;
x=x^y;
}
for(int i=log2(n);i>=0;i--)
{
if(deep[g[y][i]]>deep[x])
y=g[y][i];
}
if(deep[y]!=deep[x]) y=g[y][0];
for(int i=log2(n);i>=0;i--)
{
if(g[y][i]!=g[x][i])
{
y=g[y][i];
x=g[x][i];
}
}
if(x!=y) y=g[y][0];
return y;
}
int put(int v,int l,int r,int x,int y)
{
if(l==r)
{
tree[v].v+=y;
return 0;
}
int mid=(l+r)/2;
if(x<=mid)
{
if(!tree[v].l)
tree[v].l=++sum;
put(tree[v].l,l,mid,x,y);
}
else
{
if(!tree[v].r)
tree[v].r=++sum;
put(tree[v].r,mid+1,r,x,y);
}
tree[v].v=max(tree[tree[v].l].v,tree[tree[v].r].v);
}
int work(int x,int y,int z)
{
int lc=lca(x,y);
if(!f[x])
{
f[x]=++sum;
}
put(f[x],1,tot,z,1);
if(!f[y])
{
f[y]=++sum;
}
put(f[y],1,tot,z,1);
if(!f[lc])
{
f[lc]=++sum;
}
put(f[lc],1,tot,z,-1);
if(g[lc][0])
{
if(!f[g[lc][0]]) f[g[lc][0]]=++sum;
put(f[g[lc][0]],1,tot,z,-1);
}
}
int mesh(int x,int y,int l,int r)
{
if(l==r)
{
tree[x].v+=tree[y].v;
return 0;
}
int mid=(l+r)/2;
if(tree[y].l)
{
if(!tree[x].l)
tree[x].l=tree[y].l;
else
mesh(tree[x].l,tree[y].l,l,mid);
}
if(tree[y].r)
{
if(!tree[x].r)
tree[x].r=tree[y].r;
else
mesh(tree[x].r,tree[y].r,mid+1,r);
}
tree[x].v=max(tree[tree[x].l].v,tree[tree[x].r].v);
}
int find(int v,int l,int r)
{
if(l==r)
{
return l;
}
if(tree[tree[v].l].v==0 && tree[tree[v].r].v==0)
return 0;
int mid=(l+r)/2;
if(tree[tree[v].l].v>=tree[tree[v].r].v)
{
return find(tree[v].l,l,mid);
}
else
{
return find(tree[v].r,mid+1,r);
}
}
int merge(int x)
{
for(int i=last[x];i;i=next[i])
{
int j=to[i];
if(j!=g[x][0])
{
merge(j);
if(!f[x])
{
f[x]=++sum;
}
if(!f[j])
{
f[j]=++sum;
}
mesh(f[x],f[j],1,tot);
}
}
ans[x]=find(f[x],1,tot);
}
int main()
{
scanf("%d",&n);
scanf("%d",&m);
for(int i=1;i<=n-1;i++)
{
int x,y;
scanf("%d%d",&x,&y);
bj(x,y);
bj(y,x);
}
deep[1]=1;
dg(1);
prelca();
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&re[i].x,&re[i].y,&re[i].z);
}
sort(re+1,re+m+1,cmp);
tot=0;
sum=0;
for(int i=1;i<=m;i++)
{
if(re[i].z==t[tot])
{
re[i].z=tot;
}
else
{
t[++tot]=re[i].z;
re[i].z=tot;
}
}
for(int i=1;i<=m;i++)
{
work(re[i].x,re[i].y,re[i].z);
}
merge(1);
for(int i=1;i<=n;i++)
{
if(!f[i])
{
f[i]=++sum;
}
printf("%d\n",t[ans[i]]);
}
}
【GDOI2014模拟】雨天的尾巴的更多相关文章
- BZOJ 3307: 雨天的尾巴( LCA + 线段树合并 )
路径(x, y) +z : u处+z, v处+z, lca(u,v)处-z, fa(lca)处-z, 然后dfs一遍, 用线段树合并. O(M log M + M log N). 复杂度看起来不高, ...
- BZOJ_3307_雨天的尾巴_线段树合并+树上差分
BZOJ_3307_雨天的尾巴_线段树合并 Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y 对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成 所有发放后 ...
- [Vani有约会]雨天的尾巴 线段树合并
[Vani有约会]雨天的尾巴 LG传送门 线段树合并入门好题. 先别急着上线段树合并,考虑一下这题的暴力.一看就是树上差分,对于每一个节点统计每种救济粮的数量,再一遍dfs把差分的结果统计成答案.如果 ...
- 【BZOJ 3307】 3307: 雨天的尾巴 (线段树+树链剖分)
3307: 雨天的尾巴 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 458 Solved: 210 Description N个点,形成一个树状结 ...
- 洛谷 P4556 [Vani有约会]雨天的尾巴 解题报告
P4556 [Vani有约会]雨天的尾巴 题目背景 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒 ...
- [luogu4556]雨天的尾巴
[luogu4556]雨天的尾巴 luogu 发现是一顿子修改然后再询问,那么把修改树上差分一下再线段树合并 但是... 如果你只有35分... https://www.luogu.org/discu ...
- 【BZOJ3307】雨天的尾巴 线段树合并
[BZOJ3307]雨天的尾巴 Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多 ...
- Bzoj 3307 雨天的尾巴(线段树合并+树上差分)
C. 雨天的尾巴 题目描述 N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. 输入格式 第 ...
- [bzoj3307]雨天的尾巴_线段树合并
雨天的尾巴 bzoj-3307 题目大意:N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. ...
- 洛谷P4556 雨天的尾巴(线段树合并)
洛谷P4556 雨天的尾巴 题目链接 题解: 因为一个点可能存放多种物品,直接开二维数组进行统计时间.空间复杂度都不能承受.因为每一个点所拥有的物品只与其子树中的点有关,所以可以考虑对每一个点来建立一 ...
随机推荐
- fiddler之入门(安装配置)
在工作中常常需要进行数据的抓包和发包,此时就可以用到fiddler这个工具了. fiddler是一个http协议调试代理工具,通过http代理,让数据从其通过,来坚挺本地计算机与访问网络之间的所有ht ...
- python学习之requests基础
学习地址:http://docs.python-requests.org/zh_CN/latest/user/quickstart.html#id2 使用requests发送网络请求 一.导入requ ...
- 【MM系列】SAP 物料凭证增强
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]在SAP里查看数据的方法 前言部 ...
- 【windows】windows安全基础
windows安全基础 安全主体 security principal 是可以进行身份验证的实体. 哪个安全主体在要求访问?这个维度可以是用户,计算机和进程.一旦确认以后,系统就会发放SID. 例子: ...
- [Web 前端] 023 js 的流程控制、循环和元素的获取、操作
1. Javascript 流程控制 用于"基于不同条件执行不同的动作"的场合 1.1 if 语句 三种形式 // 第一种 if... // 第二种 if... else ... ...
- vue组件命名和传值
一.vue组件命名: 组件有好几种命名方式, 可以使用 component-vue (短横线分隔命名).componentVue (驼峰式命名) 或ComponentVue (单词首字母) 因为h ...
- @-webkit-keyframes 动画 css3
Internet Explorer 10.Firefox 以及 Opera 支持 @keyframes 规则和 animation 属性. Chrome 和 Safari 需要前缀 -webkit-. ...
- CF和OF的区别
进位标志CF和溢出标志OF的区别: 有符号数和无符号数只是认为的进行区分,计算机从来不区分有符号数和无符号数.对于运算的数来说,只要符合进位的情况,CF就置1.只要符合溢出的情况,OF就置1.但是后续 ...
- C++编译器对属性的初始化检查远没有Java严格
C++编译器对属性的初始化检查远没有Java严格// Java编译通过:class yy { public static void main(String[] args) { int i; Syste ...
- linux:输入/输出、重定向、管道
输入.输出: 程序的默认输入设备,叫标准输入. stdin 键盘 0 程序的默认输出设备,叫标准输出. stdout 监视器 1 程序的默认错误输出设备,叫标准错误输出.stde ...