题目
题目背景
题目背景
深绘里一直很讨厌雨天。
灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。
虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。
无奈的深绘里和村民们只好等待救济粮来维生。
不过救济粮的发放方式很特别。
题目描述
首先村落里的一共有 (n) 座房屋,并形成一个树状结构。然后救济粮分 (m) 次发放,每次选择两个房屋 ((x,~y)),然后对于 (x) 到 (y) 的路径上(含 (x) 和 (y))每座房子里发放一袋 (z) 类型的救济粮。
然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。
输入格式
输入的第一行是两个用空格隔开的正整数,分别代表房屋的个数 (n) 和救济粮发放的次数 (m)。
第 (2) 到 第 (n) 行,每行有两个用空格隔开的整数 (a,~b),代表存在一条连接房屋 (a) 和 (b) 的边。
第 ((n + 1)) 到第 ((n + m)) 行,每行有三个用空格隔开的整数 (x,~y,~z),代表一次救济粮的发放是从 (x) 到 (y) 路径上的每栋房子发放了一袋 (z) 类型的救济粮。
输出格式
输出 (n) 行,每行一个整数,第 (i) 行的整数代表 (i) 号房屋存放最多的救济粮的种类,如果有多种救济粮都是存放最多的,输出种类编号最小的一种。
如果某座房屋没有救济粮,则输出 (0)。
输入输出样例
输入 #1
5 3 1 2 3 1 3 4 5 3 2 3 3 1 5 2 3 3 3
输出 #1
2 3 3 0 2
说明/提示
- 对于 (20\%) 的数据,保证 (n, m leq 100)。
- 对于 (50\%) 的数据,保证 (n, m leq 2 imes 10^3)。
- 对于 (100\%) 测试数据,保证 (1 leq n, m leq 10^5),(1 leq a,b,x,y leq n),(1 leq z leq 10^5)。
思路
若用(a_{i,j})表示结点(i)上救济粮(j)的数量,则结点(i)的答案就是(k),满足(a_{i,k})最大,(k)最小,即最值所在位置.
我们考虑建一个(a)的(树上)差分数组(b):
但是一个(i)一个(i)求,还要维护最值,肯定是爆时空复杂度的.
在看看问题,一次发救济粮,就改了几个数字:(b_{x,z} , b_{y,z} , b_{lca(x,y),z} , b_{fa(lca(x,y)),z})(具体参考树上差分),也就是说(b)数组很多都是空的.所以,我们用 动态开点 + 线段树合并 来维护(a,b),具体看代码.
代码
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int read() {
int re = 0;
char c = getchar();
bool negt = false;
while(c < '0' || c > '9')
negt |= (c == '-') , c = getchar();
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0' , c = getchar();
return negt ? -re : re;
}
const int N = 100010;
const int maxZ = 100000;
const int M = 100010;
const int M_logZ = M * 20;
struct EDGE {
int to , nxt;
} ed[N * 2];
int head[N];
void addedge(int u , int v) {
static int cnt;
++cnt;
ed[cnt].to = v , ed[cnt].nxt = head[u] , head[u] = cnt;
}
int n , m , root;
int father[N];
namespace LCA {//RMQ-LCA,LCA就对了
int cnt = 0;
int dep[N * 2] , id[N * 2] , first[N];
int st[N * 4][25];
void dfs(int u , int nowdep) {
++cnt;
id[cnt] = u , dep[cnt] = nowdep , first[u] = cnt;
for(int i = head[u] ; i ; i = ed[i].nxt) {
int v = ed[i].to;
if(v == father[u])
continue;
father[v] = u;
dfs(v , nowdep + 1);
++cnt;
id[cnt] = u , dep[cnt] = nowdep;
}
}
void init(int root) {
dfs(root , 1);
for(int i = 1 ; i <= cnt ; i++)
st[i][0] = i;
int k = log(n) / log(2) + 1;
dep[0] = 0x3fffffff;
for(int j = 1 ; j <= k ; j++)
for(int i = 1 ; i <= cnt ; i++)
st[i][j] = dep[st[i][j - 1]] < dep[st[i + (1 << j - 1)][j - 1] ] ? st[i][j - 1] : st[i + (1 << j - 1)][j - 1];
}
int lca(int u , int v) {
u = first[u] , v = first[v];
if(u > v) {
int tmp;
tmp = u , u = v , v = tmp;
}
int k = log(v - u + 1) / log(2);
return id[
dep[st[u][k]] < dep[st[v - (1 << k) + 1][k]] ? st[u][k] : st[v - (1 << k) + 1][k]
];
}
}
inline int dep(int u) {
return LCA::dep[LCA::first[u]];
}
namespace SegT {//我的一个代码习惯:函数中的p表示当前递归到的线段树结点的编号
int root[N];
struct NodeClass {
int l , r , ls , rs , dat_num , dat_id;
//当前区间[l,r] , ls/rs:LeftSon,RightSon左右子结点编号 , dat_num:区间最值(存放最多的救济粮的数量) ,dat_id:区间最值出现的位置(该救济粮的种类)
} node[4 * M_logZ];//其实我也不知道数组要开多大QAQ
inline int newnode (int l , int r) {//开点
static int cnt = 0;
++cnt;
node[cnt].l = l , node[cnt].r = r , node[cnt].dat_id = l;
return cnt;
}
inline void push_up(int p) {//递归子结点后,更新结点p的数据
#define ls(p) node[p].ls
#define rs(p) node[p].rs
int tmp;
if(ls(p) == 0)
tmp = rs(p);
else if(rs(p) == 0)
tmp = ls(p);
else if(node[ls(p)].dat_num >= node[rs(p)].dat_num)//注意等号
tmp = ls(p);
else
tmp = rs(p);
node[p].dat_id = node[tmp].dat_id , node[p].dat_num = node[tmp].dat_num;
#undef ls
#undef rs
}
void change(int p , int pos , int dat) {
if(node[p].l == node[p].r) {
node[p].dat_num += dat;
return;
}
int mid = (node[p].l + node[p].r) / 2;
if(pos <= mid)//这里比较压行,其实就是先检查左/右子树是否为空,若为空,先开一个结点,然后递归
change(node[p].ls = (node[p].ls == 0 ? newnode(node[p].l , mid) : node[p].ls) , pos , dat);
else
change(node[p].rs = (node[p].rs == 0 ? newnode(mid + 1 , node[p].r) : node[p].rs) , pos , dat);
push_up(p);
}
int merge(int p1 , int p2 , int l , int r) {//合并以p1,p2为根的线段树
if(p1 == 0 || p2 == 0)
return p1 + p2;
if(l == r) {
node[p1].dat_num += node[p2].dat_num;
return p1;
}
int mid = (l + r) / 2;
node[p1].ls = merge(node[p1].ls , node[p2].ls , l , mid);
node[p1].rs = merge(node[p1].rs , node[p2].rs , mid + 1 , r);
push_up(p1);
return p1;
}
}
int ans[N];
void calc(int u) {
for(int i = head[u] ; i ; i = ed[i].nxt) {
int v = ed[i].to;
if(v == father[u])
continue;
calc(v);
SegT::root[u] = SegT::merge(SegT::root[u] , SegT::root[v] , 1 , maxZ);
}
ans[u] = SegT::node[SegT::root[u]].dat_num == 0 ? 0 : SegT::node[SegT::root[u]].dat_id;
}
int main() {
n = read() , m = read() , root = 1;
for(int i = 1 ; i < n ; i++) {
int u = read() , v = read();
addedge(u , v) , addedge(v , u);
}
LCA::init(root);
for(int i = 1 ; i <= n ; i++)
SegT::root[i] = SegT::newnode(1 , maxZ);
for(int i = 1 ; i <= m ; i++) {
int u = read() , v = read() , z = read();
int lca = LCA::lca(u , v);
SegT::change(SegT::root[u] , z , 1);
SegT::change(SegT::root[v] , z , 1);
SegT::change(SegT::root[lca] , z , -1);
SegT::change(SegT::root[father[lca]] , z , -1);
}
calc(root);
for(int i = 1 ; i <= n ; i++)
printf("%d
" , ans[i]);
return 0;
}