题目链接

吐槽

这道题调了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. 创建虚拟目录失败,必须为服务器名称指定“localhost”

    关于微信开发过程,远程调试后,再次打开vs出现项目加载失败的解决办法! 第一步: 第二步:打开编辑的页面,把下图这部分直接注释掉 ok了,再加载一次,就好了!

  2. C#读书笔记:线程,任务和同步

    前言 学习C#两个多月了,像当初实习做PHP开发一样,也是由着一个个Feature需求,慢慢掌握了很多相关的编程技巧.本次主要记录下学习C# 多线程的相关知识. 参考书籍:<Csharp高级编程 ...

  3. JS 类型检测

    typeof 适合函数对象和基本类型的判断 typeof 100instanceof 适合判断对象类型 obj instanceof Object 基于原型链判断操作符,若做操作符不是对象,则会直接返 ...

  4. web项目中的监听器,过滤器以及自定义servlet的执行顺序

    可以看到web容器一启动就会实例化监听器的contextInitialized(ServletContextEvent event)方法,然后是过滤器的init()方法,最后在用户访问web应用的 时 ...

  5. 二进制安装MongoDB

    1.下载mongodb cd /usr/local/src/ wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.5.tgz ...

  6. docker学习笔记一

    知识点: 1)docker简介 2)docker安装,仓库配置 3)docker仓库镜像拉取,导出,导入,删除 4)docker容器操作,容器的创建,删除,运行,停止,日志查看等. 5)  docke ...

  7. c++ string替换指定字符串

    string fnd = "dataset"; string rep = "labels"; string buf = "d:/data/datase ...

  8. ubuntu6.04安装

    一.在windows操作系统下准备ubuntu系统的安装盘 1. 下载ubuntu的ISO文件 这一步相对简单,网络上面有很多的链接下载.这里贴一个ubuntu的官方网站链接,可以下载到ubuntu ...

  9. BZOJ4032[HEOI2015]最短不公共子串——序列自动机+后缀自动机+DP+贪心

    题目描述 在虐各种最长公共子串.子序列的题虐的不耐烦了之后,你决定反其道而行之. 一个串的“子串”指的是它的连续的一段,例如bcd是abcdef的子串,但bde不是. 一个串的“子序列”指的是它的可以 ...

  10. #186 path(容斥原理+状压dp+NTT)

    首先只有一份图时显然可以状压dp,即f[S][i]表示S子集的哈密顿路以i为终点的方案数,枚举下个点转移. 考虑容斥,我们枚举至少有多少条原图中存在的边(即不合法边)被选进了哈密顿路,统计出这个情况下 ...