题目链接

吐槽

这道题调了7个小时也是够了。最后只好比着题解做了一遍2333

思路

首先考虑n=2000的情况。因为这是在一条路径上,所以可以考虑差分。用a[i][j]表示第i个点中j这种粮食出现的次数。加入要在从x到y的路径上加入c这种粮食。将这条路径分为两部分进行差分。从x到lca,也就是将a[x][c]++,a[fa[lca]]--。从y到son[lca]。就是将a[y][c]++,a[lca]--。(详细原因见树上差分)最后dfs一遍,并且在回溯的时候合并一下就行了。

那么对于n=100000的数据,只要用线段树合并优化一下就行了。也就是将a数组改为n棵动态开点线段树。合并的时候用线段树合并的板子合并一下就行了。

代码

/*
* @Author: wxyww
* @Date: 2018-12-10 14:37:11
* @Last Modified time: 2018-12-10 21:15:06
*/
#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
#define Ls TR[cur].ls
#define Rs TR[cur].rs
ll read() {
ll x = 0,f = 1;char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
const int N = 100000 + 100,logN = 20;
struct node {
int ls,rs,ans,sum;
}TR[N * 50];
int n,m,dep[N],lca[N][logN + 30],root[N];
vector<int>e[N];
int num;
void get_lca(int u,int father) {
for(int i = 1;i < logN;++i) lca[u][i] = lca[lca[u][i - 1]][i - 1];
int k = e[u].size();
for(int i = 0;i < k;++i) {
int v = e[u][i];
if(v == father) continue;
lca[v][0] = u;
dep[v] = dep[u] + 1;
get_lca(v,u);
}
}
int LCA(int x,int y) {
if(dep[x] < dep[y]) swap(x,y);
for(int i = logN - 1;i >= 0;--i)
if(dep[lca[x][i]] >= dep[y]) x = lca[x][i];
if(x == y) return y;
for(int i = logN - 1;i >= 0;--i) {
if(lca[x][i] != lca[y][i]) {
x = lca[x][i];y = lca[y][i];
}
}
return lca[x][0];
}
void up(int cur) {
if(TR[Ls].sum >= TR[Rs].sum) {
TR[cur].sum = TR[Ls].sum;
TR[cur].ans = TR[Ls].ans;
}
else {
TR[cur].sum = TR[Rs].sum;
TR[cur].ans = TR[Rs].ans;
}
}
void update(int &cur,int l,int r,int pos,int c) {
if(!cur) cur = ++num;
if(l == r) {
TR[cur].sum += c;
TR[cur].ans = l;
return;
}
int mid = (l + r) >> 1;
if(pos <= mid) update(Ls,l,mid,pos,c);
else update(Rs,mid + 1,r,pos,c);
up(cur);
}
int merge(int cur,int a,int l,int r) {
if(!cur) return a;
if(!a) return cur;
if(l == r) {
TR[cur].sum += TR[a].sum;
TR[cur].ans = l;
return cur;
}
int mid = (l + r) >> 1;
Ls = merge(Ls,TR[a].ls,l,mid);
Rs = merge(Rs,TR[a].rs,mid + 1,r);
up(cur);
return cur;
}
void dfs(int u,int father) {
int k = e[u].size();
for(int i = 0;i < k;++i) {
int v = e[u][i];
if(v == father) continue;
dfs(v,u);
merge(root[u],root[v],1,N - 100);
}
}
int main() {
int n = read(),m = read();
for(int i = 1;i < n;++i) {
int x = read(),y = read();
e[x].push_back(y);
e[y].push_back(x);
}
dep[1] = 1;
for(int i = 1;i <= n;++i) root[i] = ++num;
get_lca(1,0);
while(m--) {
int x = read(),y = read(),c = read();
int K = LCA(x,y);
update(root[x],1,N - 100,c,1);
update(root[y],1,N - 100,c,1);
update(root[K],1,N - 100,c,-1);
if(lca[K][0]) update(root[lca[K][0]],1,N - 100,c,-1);
}
dfs(1,0);
for(int i = 1;i <= n;++i) {
if(TR[root[i]].sum == 0) puts("0");
else
printf("%d\n",TR[root[i]].ans);
} return 0;
}

[luogu4556][Vani有约会]的更多相关文章

  1. 「Luogu4556」Vani有约会-雨天的尾巴

    「Luogu4556」Vani有约会-雨天的尾巴 传送门 很显然可以考虑树上差分+桶,每次更新一条链就是把这条链上的点在桶对应位置打上 \(1\) 的标记, 最后对每个点取桶中非零值的位置作为答案即可 ...

  2. [Vani有约会]雨天的尾巴 线段树合并

    [Vani有约会]雨天的尾巴 LG传送门 线段树合并入门好题. 先别急着上线段树合并,考虑一下这题的暴力.一看就是树上差分,对于每一个节点统计每种救济粮的数量,再一遍dfs把差分的结果统计成答案.如果 ...

  3. 洛谷 P4556 [Vani有约会]雨天的尾巴 解题报告

    P4556 [Vani有约会]雨天的尾巴 题目背景 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒 ...

  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有约会]雨天的尾巴

    [题解] P4556 [Vani有约会]雨天的尾巴 ·题目大意 给定一棵树,有m次修改操作,每次修改 \(( x\) \(y\) \(z )\) 表示 \((x,y)\) 之间的路径上数值 \(z\) ...

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

    题目背景 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地 ...

  8. [Vani有约会]雨天的尾巴

    嘟嘟嘟 看到链上操作,自然想到树剖. 先考虑序列上的问题:那么区间修改可以用差分.所以我们把操作拆成\(L\)和\(R + 1\)两个点,然后离线.排序后扫一遍,用线段树维护数量最多的颜色是哪一个. ...

  9. [Vani有约会]雨天的尾巴(树上差分+线段树合并)

    首先村落里的一共有n座房屋,并形成一个树状结构.然后救济粮分m次发放,每次选择两个房屋(x,y),然后对于x到y的路径上(含x和y)每座房子里发放一袋z类型的救济粮. 然后深绘里想知道,当所有的救济粮 ...

随机推荐

  1. C# Note34: 异常机制相关小点

    1.使用throw和throw ex抛出异常的区别 通常,我们使用try/catch/finally语句块来捕获异常,那么在抛出异常的时候,使用throw和throw ex有什么区别呢? 假如,按顺序 ...

  2. SOAP-ERROR: Encoding: string … is not a valid utf-8 string

    今天遇到一个错误,看标题就知道是什么错误了.... 最坑爹的是,不是所有的用户会报这个错误.只有少部分.在生产环境又没办法调试. 找了半天都不知道什么原因,字面意思大概是需要一个utf8编码的字符串, ...

  3. MySQL执行语句的顺序

    MySQL的语句一共分为11步,最先执行的总是FROM操作,最后执行的是LIMIT操作.其中每一个操作都会产生一张虚拟的表,这个虚拟的表作为一个处理的输入,只是这些虚拟的表对用户来说是透明的,但是只有 ...

  4. 关于mysql远程登录问题

    问题:mysql不能实现远程登录 前提:mysql开启了远程登录账号,安全组也放行了3306,防火墙是iptables,也加入了3306放行,但是还是不能实现远程访问 解决办法,使用iptables ...

  5. github & markdown & collapse & table

    github & markdown collapse & table https://github.com/Microsoft/TypeScript/issues/30034 GitH ...

  6. vue樣式綁定

    vue的樣式可以使得class,style不僅可以綁定文本,而且可以綁定數組和對象. 使用對象{} 使用數組 綁定對象 使用computed屬性, 使用內聯樣式.

  7. SpringBoot之整合Mybatis范例

    依赖包: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http:/ ...

  8. C#使用MemoryStream类读写内存

    MemoryStream和BufferedStream都派生自基类Stream,因此它们有很多共同的属性和方法,但是每一个类都有自己独特的用法.这两个类都是实现对内存进行数据读写的功能,而不是对持久性 ...

  9. WinForm中在非UI线程更改控件值的办法

    从非UI线程调用UI控件赋值.或进行其他更新UI的操作的话,会出现异常: System.InvalidOperationException:“线程间操作无效: 从不是创建控件“xxx”的线程访问它.” ...

  10. essential-phone的相关体验

    一.adb环境配置 1.下载adb工具 工具网上一搜一大把,注意路径不能有中文. 2.系统配置环境变量 找到环境变量,点击新建.变量名根据自己的习惯随便建,变量值为下载的adb工具解压后存放的路径. ...