路径(x, y) +z : u处+z, v处+z, lca(u,v)处-z, fa(lca)处-z, 然后dfs一遍, 用线段树合并. O(M log M + M log N). 复杂度看起来不高, 但是跑起来很慢.

另一种做法是先树链剖分, 转成序列上的情况, 然后依旧是差分+线段树维护, O(M log N log M). 但是实际跑起来好像更快...

------------------------------------------------------------------------------

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
 
using namespace std;
 
const int maxn = 100009;
 
int N, M;
int Hash[maxn], Hn;
 
template<class T>
inline void Min(T &x, T t) {
if(t < x) x = t;
}
template<class T>
inline void Max(T &x, T t) {
if(t > x) x = t;
}
 
inline int getint() {
char c = getchar();
for(; !isdigit(c); c = getchar());
int ret = 0;
for(; isdigit(c); c = getchar())
ret = ret * 10 + c - '0';
return ret;
}
 

struct L {

int x;
L* n;
} Lpool[maxn * 4], *Pt = Lpool, *Head[maxn];
 
inline void AddL(int a, int b) {
Pt->x = b, Pt->n = Head[a], Head[a] = Pt++;
}
 
struct O {
int x, y, z;
} o[maxn];
 
struct edge {
int t;
edge* n;
} E[maxn << 1], *ept = E, *H[maxn];
 
inline void AddEdge(int u, int v) {
ept->t = v, ept->n = H[u], H[u] = ept++;
}
 
void Init() {
Hn = 0;
int u, v;
N = getint(), M = getint();
for(int i = 1; i < N; i++) {
u = getint() - 1, v = getint() - 1;
AddEdge(u, v);
AddEdge(v, u);
}
for(int i = 0; i < M; i++) {
o[i].x = getint() - 1;
o[i].y = getint() - 1;
Hash[Hn++] = o[i].z = getint();
}
Hash[Hn++] = 0;
sort(Hash, Hash + Hn);
Hn = unique(Hash, Hash + Hn) - Hash;
for(int i = 0; i < M; i++)
o[i].z = lower_bound(Hash, Hash + Hn, o[i].z) - Hash;
}
 
int top[maxn], sz[maxn], ch[maxn], fa[maxn], dep[maxn];
int Top;
 
void dfs(int x) {
sz[x] = 1, ch[x] = -1;
for(edge* e = H[x]; e; e = e->n) if(e->t != fa[x]) {
dep[e->t] = dep[x] + 1;
fa[e->t] = x;
dfs(e->t);
sz[x] += sz[e->t];
if(!~ch[x] || sz[ch[x]] < sz[e->t]) ch[x] = e->t;
}
}
 
void DFS(int x) {
top[x] = Top;
if(~ch[x]) DFS(ch[x]);
for(edge* e = H[x]; e; e = e->n)
if(e->t != fa[x] && e->t != ch[x]) DFS(Top = e->t);
}
 
int LCA(int x, int y) {
for(; top[x] != top[y]; x = fa[top[x]])
if(dep[top[x]] < dep[top[y]]) swap(x, y);
return dep[x] < dep[y] ? x : y;
}
 
struct Node {
Node *lc, *rc;
int mx, Id;
inline void upd() {
mx = -maxn, Id = maxn;
if(lc) Max(mx, lc->mx);
if(rc) Max(mx, rc->mx);
if(lc && lc->mx == mx) Min(Id, lc->Id);
if(rc && rc->mx == mx) Min(Id, rc->Id);
}
} pool[maxn * 70], *pt = pool, *V[maxn];
 
int Val, Pos;
 
void Modify(Node*&t, int l, int r) {
if(!t)
(t = pt++)->mx = 0;
int m = (l + r) >> 1;
if(l == r) {
t->mx += Val;
t->Id = m;
} else {
Pos <= m ? Modify(t->lc, l, m) : Modify(t->rc, m + 1, r);
t->upd();
}
if(!t->mx) t = 0;
}
 
Node* Merge(Node* a, Node* b, int l, int r) {
int m = (l + r) >> 1;
if(!a) return b;
if(!b) return a;
if(l != r) {
a->lc = Merge(a->lc, b->lc, l, m);
a->rc = Merge(a->rc, b->rc, m + 1, r);
a->upd();
} else
a->mx += b->mx;
return a;
}
 
int ans[maxn];
 
void calc(int x) {
for(L* t = Head[x]; t; t = t->n) {
if(t->x > 0)
Pos = t->x, Val = 1;
else
Pos = -t->x, Val = -1;
Modify(V[x], 1, Hn);
}
for(edge* e = H[x]; e; e = e->n) if(e->t != fa[x]) {
calc(e->t);
V[x] = Merge(V[x], V[e->t], 1, Hn);
}
ans[x] = V[x] ? V[x]->Id : 0;
}
 
void Work() {
fa[0] = -1;
dep[0] = 0;
dfs(0);
DFS(Top = 0);
for(int i = 0; i < M; i++) {
int lca = LCA(o[i].x, o[i].y);
AddL(o[i].x, o[i].z);
AddL(o[i].y, o[i].z);
AddL(lca, -o[i].z);
if(~fa[lca]) AddL(fa[lca], -o[i].z);
}
calc(0);
for(int i = 0; i < N; i++)
printf("%d\n", Hash[ans[i]]);
}
 

int main() {

Init();
Work();
return 0;
}

------------------------------------------------------------------------------

3307: 雨天的尾巴

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 267  Solved: 127
[Submit][Status][Discuss]

Description

N个点,形成一个树状结构。有M次发放,每次选择两个点x,y
对于x到y的路径上(含x,y)每个点发一袋Z类型的物品。完成
所有发放后,每个点存放最多的是哪种物品。

Input

第一行数字N,M
接下来N-1行,每行两个数字a,b,表示a与b间有一条边
再接下来M行,每行三个数字x,y,z.如题

Output

输出有N行
每i行的数字表示第i个点存放最多的物品是哪一种,如果有
多种物品的数量一样,输出编号最小的。如果某个点没有物品
则输出0

Sample Input

20 50
8 6
10 6
18 6
20 10
7 20
2 18
19 8
1 6
14 20
16 10
13 19
3 14
17 18
11 19
4 11
15 14
5 18
9 10
12 15
11 14 87
12 1 87
14 3 84
17 2 36
6 5 93
17 6 87
10 14 93
5 16 78
6 15 93
15 5 16
11 8 50
17 19 50
5 4 87
15 20 78
1 17 50
20 13 87
7 15 22
16 11 94
19 8 87
18 3 93
13 13 87
2 1 87
2 6 22
5 20 84
10 12 93
18 12 87
16 10 93
8 17 93
14 7 36
7 4 22
5 9 87
13 10 16
20 11 50
9 16 84
10 17 16
19 6 87
12 2 36
20 9 94
9 2 84
14 1 94
5 5 94
8 17 16
12 8 36
20 17 78
12 18 50
16 8 94
2 19 36
10 18 36
14 19 50
4 12 50

Sample Output

87
36
84
22
87
87
22
50
84
87
50
36
87
93
36
94
16
87
50
50

1<=N,M<=100000
1<=a,b,x,y<=N
1<=z<=10^9

HINT

Source

BZOJ 3307: 雨天的尾巴( LCA + 线段树合并 )的更多相关文章

  1. Bzoj 3307 雨天的尾巴(线段树合并+树上差分)

    C. 雨天的尾巴 题目描述 N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. 输入格式 第 ...

  2. BZOJ_3307_雨天的尾巴_线段树合并+树上差分

    BZOJ_3307_雨天的尾巴_线段树合并 Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y 对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成 所有发放后 ...

  3. 【BZOJ 3307】 3307: 雨天的尾巴 (线段树+树链剖分)

    3307: 雨天的尾巴 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 458  Solved: 210 Description N个点,形成一个树状结 ...

  4. P4556 [Vani有约会]雨天的尾巴(线段树合并+lca)

    P4556 [Vani有约会]雨天的尾巴 每个操作拆成4个进行树上差分,动态开点线段树维护每个点的操作. 离线处理完向上合并就好了 luogu倍增lca被卡了5分.....于是用rmq维护.... 常 ...

  5. P4556 [Vani有约会]雨天的尾巴 (线段树合并)

    P4556 [Vani有约会]雨天的尾巴 题意: 首先村落里的一共有n座房屋,并形成一个树状结构.然后救济粮分m次发放,每次选择两个房屋(x,y),然后对于x到y的路径上(含x和y)每座房子里发放一袋 ...

  6. P4556 [Vani有约会]雨天的尾巴(线段树合并)

    传送门 一道线段树合并 首先不难看出树上差分 我们把每一次修改拆成四个,在\(u,v\)分别放上一个,在\(lca\)和\(fa[lca]\)各减去一个,那么只要统计一下子树里的总数即可 然而问题就在 ...

  7. 雨天的尾巴(bzoj3307)(线段树合并+树上差分)

    \(N\)个点,形成一个树状结构.有\(M\)次发放,每次选择两个点\(x,y\) 对于\(x\)到\(y\)的路径上(含\(x,y\))每个点发一袋\(Z\)类型的物品.完成 所有发放后,每个点存放 ...

  8. 洛谷P4556 雨天的尾巴(线段树合并)

    洛谷P4556 雨天的尾巴 题目链接 题解: 因为一个点可能存放多种物品,直接开二维数组进行统计时间.空间复杂度都不能承受.因为每一个点所拥有的物品只与其子树中的点有关,所以可以考虑对每一个点来建立一 ...

  9. [bzoj3307]雨天的尾巴_线段树合并

    雨天的尾巴 bzoj-3307 题目大意:N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. ...

随机推荐

  1. 程序员使用Node的十个技巧

    从问世到现在将近20年,JavaScript一直缺乏其它有吸引力的编程语言,比如Python和Ruby,的很多优点:命令行界面,REPL,包管理器,以及组织良好的开源社区.感谢Node.js和npm, ...

  2. HDU 5791 Two

    题意:给两个序列,求公共序列的个数 分析:很自然想到最长公共子序列的转移的转移形式,用dp[i][j]表示第一个串前i个 和第二个串前j个匹配的答案数量,a[i]==b[i],dp[i][j]=dp[ ...

  3. 单击事件的处理方式及注册窗体的创建之(四)Intent实现界面跳转传值

    跳转开发步骤: 创建Intent对象  设置Intent对象的跳转路径  启动Intent //Register_Activity.java case R.id.btnRegister: Inte ...

  4. tablbView中section的间距

    - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { if (sect ...

  5. struts2 <s:iterator> 遍历方法

    1.MapAction.java import java.util.ArrayList;   import java.util.HashMap;   import java.util.List;    ...

  6. HNOI2004 宠物收养所 解题报告

    首先读完这题第一印象,是个裸题,很高兴.其次在打完代码之后,第二印象,很恶心,Treap的代码太长了,我今天下午敲了三遍,手都麻了. 废话不多说,正题.其实这个题不难,有几个点是很好的,首先,他的a值 ...

  7. IRP 与 派遣函数

    什么是派遣函数: 派遣函数是 WIndows 驱动程序中的重要概念.驱动程序的主要功能是负责处理I/O请求,其中大部分I/O请求是在派遣函数中处理的.也就是说,派遣函数是用来处理驱动程序提交过来的 I ...

  8. Go语言中怎样判断数据类型_不懂的木匠_新浪博客

    要判断数据类型,可以用Go的空接口: 建一个函数t 设置参数i 的类型为空接口,空接口可以接受任何数据类型 func t(i interface{}) {  //函数t有一个参数i  switch i ...

  9. 关于dwt文件和lbi文件

    1,dwt 文件是网页模板文件(Dreamweaver Template), 在创建网站的多个网页的时候,通常可以将网页的共同部分创建成为一个模板, 然后给多个网页调用, 以实现网页代码的重复利用. ...

  10. php number_format()保留小数点后几位有效数的函数 千位分组来格式化数字(转)

    PHP保留小数点后2位的函数number_format number_format(带小数点的书,小数点后保留的位数) number_format(8.3486,2);  //取得小数点后2位有效数/ ...