@description@

给定一个 N 个点的树,第 i 条边连接 ai 与 bi,颜色为 ci,边权为 di。

现有 Q 个询问,每次询问假设将颜色为 xi 的边的边权全部修改为 yi 时,ui 到 vi 这一条路径上的边权和。

Constraints

2≤N≤10^5, 1≤Q≤10^5

1≤ai,bi,uj,vj≤N

1≤ci,xj≤N−1

1≤di,yj≤10^4

保证给出的图是树。保证所有输入值为整数。

Input

如下:

N Q

a1 b1 c1 d1

:

aN−1 bN-1 cN−1 dN−1

x1 y1 u1 v1

:

xQ yQ uQ vQ

Output

输出 Q 行,每行输出对应询问的答案。

Sample Input 1

5 3

1 2 1 10

1 3 2 20

2 4 4 30

5 2 1 40

1 100 1 4

1 100 1 5

3 1000 3 4

**Sample Output 1 **

130

200

60

@solution - 1@

跟颜色有关,虽然题目中说是要“修改”,但实际上每一个询问之间是独立的,即修改不会对后面的询问产生影响。

所以不难想到可以使用树上莫队。

首先将边的颜色与边权下放至点。然后莫队时,统计出区间内每个点的点权(就是下放的边权)和 S、区间每种颜色的出现次数 cnt[i]、区间每种颜色的边权和 s[i]。

查询时答案即为 S - s[x] + y*cnt[x]。

有关树上莫队的实现方法,你可以在yhn学长的这篇博客里找到详情。

稍微简述一下树上莫队吧,就当作复习。

树上莫队虽然有直接树上分块的方法,但是实现起来较为麻烦,所以一般来讲会写将树转为括号序列然后再进行线性序列上的莫队。但是这个方法因为要将树转为括号序列,所以常数要翻一倍,对于有些题目可能会比较卡常。

转为括号序列怎么提取一条链的信息呢?注意我们的莫队算法是一个一个元素加入而不是整体提取区间信息,所以我们可以使某个区间内,有用的点出现奇数次,无用的点出现偶数次。然后每一次元素是加入还是删除就观察它的出现次数而决定。

令 fir[x] 表示 x 对应的左括号,bac[x] 表示 x 对应的右括号。讨论一下 u, v 是否为祖先关系:

如果是,不妨假设 u 是 v 的祖先。则我们需要的区间即 [fir[u], fir[v]]。

如果不是,不妨假设 u 在 v 之前出现。则我们需要的区间即 [bac[u], fir[v]]。注意这种情况需要特殊处理 u, v 的 lca。

实现时可以通过某些技巧避免 lca 的 问题。

@accepted code - 1@

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 100000;
const int BLOCK = 450;
struct edge{
int to, dis, clr;
edge *nxt;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt = &edges[0];
void addedge(int u, int v, int c, int d) {
edge *p = (++ecnt);
p->to = v, p->clr = c, p->dis = d;
p->nxt = adj[u], adj[u] = p;
p = (++ecnt);
p->to = u, p->clr = c, p->dis = d;
p->nxt = adj[v], adj[v] = p;
}
int clr[MAXN + 5], dis[MAXN + 5];
int fa[MAXN + 5][20], dep[MAXN + 5];
int dfn[2*MAXN + 5], fir[MAXN + 5], bac[MAXN + 5], dcnt = 0;
void dfs(int x, int pre) {
dfn[++dcnt] = x, fir[x] = dcnt;
fa[x][0] = pre, dep[x] = dep[pre] + 1;
for(int i=1;i<20;i++)
fa[x][i] = fa[fa[x][i-1]][i-1];
for(edge *p=adj[x];p;p=p->nxt)
if( p->to != pre ) {
dfs(p->to, x);
clr[p->to] = p->clr, dis[p->to] = p->dis;
}
dfn[++dcnt] = x, bac[x] = dcnt;
}
int lca(int u, int v) {
if( dep[u] < dep[v] ) swap(u, v);
for(int i=19;i>=0;i--)
if( dep[fa[u][i]] >= dep[v] )
u = fa[u][i];
if( u == v ) return u;
for(int i=19;i>=0;i--)
if( fa[u][i] != fa[v][i] )
u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
struct query{
int le, ri, num;
int x, y;
}qry[MAXN + 5];
bool operator < (query a, query b) {
return (a.le/BLOCK == b.le/BLOCK) ? a.ri < b.ri : a.le < b.le;
}
int nwres, cnt[MAXN + 5], tag[MAXN + 5];
int sum[MAXN + 5], ans[MAXN + 5];
void update(int x) {
int y = dfn[x];
if( tag[y] )
cnt[clr[y]]--, sum[clr[y]] -= dis[y], nwres -= dis[y];
else cnt[clr[y]]++, sum[clr[y]] += dis[y], nwres += dis[y];
tag[y] ^= 1;
}
int main() {
int N, Q; scanf("%d%d", &N, &Q);
for(int i=1;i<N;i++) {
int a, b, c, d; scanf("%d%d%d%d", &a, &b, &c, &d);
addedge(a, b, c, d);
}
dfs(1, 0);
for(int i=1;i<=Q;i++) {
int x, y, u, v; scanf("%d%d%d%d", &x, &y, &u, &v);
int l = lca(u, v);
if( l == u || l == v ) {
if( l == v ) swap(u, v);
qry[i].le = fir[u] + 1, qry[i].ri = fir[v];
}
else {
if( fir[u] > fir[v] ) swap(u, v);
qry[i].le = bac[u], qry[i].ri = fir[v];
}
qry[i].num = i, qry[i].x = x, qry[i].y = y;
}
sort(qry + 1, qry + Q + 1);
int l = 1, r = 0;
for(int i=1;i<=Q;i++) {
while( l > qry[i].le ) update(--l);
while( r < qry[i].ri ) update(++r);
while( l < qry[i].le ) update(l++);
while( r > qry[i].ri ) update(r--);
ans[qry[i].num] = nwres - sum[qry[i].x] + cnt[qry[i].x]*qry[i].y;
}
for(int i=1;i<=Q;i++)
printf("%d\n", ans[i]);
}

@solution - 2@

你说要强制在线其实也可以,而且复杂度可能还比莫队优秀(只是不方便直接粘贴模板)

首先我们可以通过简单的差分将问题拆成若干点到根的链询问,然后跟莫队一样考虑求某种颜色的出现次数以及权值和。

注意从某个点的父亲到这个点时最多只会加入一种颜色的一条边,所以我们可以写一个可持久化数组(从实现的功能来说它的确是可持久化数组,但从实现的方式来说你只能写可持久化线段树)。

然后从父亲的版本通过加一条父亲到自己的边的信息得到当前点的版本。

查询时直接查询,没什么问题。

@accepted code - 2@

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 100000;
typedef pair<int, int> pii;
struct segtree{
struct node{
int cnt, sum;
node *ch[2];
}pl[20*MAXN + 5], *NIL, *ncnt;
segtree(){NIL = ncnt = &pl[0]; NIL->ch[0] = NIL->ch[1] = NIL;}
node *add(node *pre, int l, int r, int pos, int del) {
node *nw = (++ncnt); (*nw) = (*pre);
if( l == r ) {
nw->cnt++, nw->sum += del;
return nw;
}
int mid = (l + r) >> 1;
if( pos <= mid ) nw->ch[0] = add(pre->ch[0], l, mid, pos, del);
else nw->ch[1] = add(pre->ch[1], mid + 1, r, pos, del);
return nw;
}
pii query(node *nw, int l, int r, int pos) {
if( l == r )
return make_pair(nw->cnt, nw->sum);
int mid = (l + r) >> 1;
if( pos <= mid ) return query(nw->ch[0], l, mid, pos);
else return query(nw->ch[1], mid + 1, r, pos);
}
}T;
segtree::node *rt[MAXN + 5];
struct edge{
int dis, clr, to;
edge *nxt;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt=&edges[0];
void addedge(int u, int v, int c, int d) {
edge *p = (++ecnt);
p->to = v, p->clr = c, p->dis = d;
p->nxt = adj[u], adj[u] = p;
p = (++ecnt);
p->to = u, p->clr = c, p->dis = d;
p->nxt = adj[v], adj[v] = p;
}
int N, Q;
int fa[20][MAXN + 5], dep[MAXN + 5];
int dis[MAXN + 5];
void dfs(int x, int f) {
fa[0][x] = f;
for(int i=1;i<20;i++)
fa[i][x] = fa[i-1][fa[i-1][x]];
dep[x] = dep[f] + 1;
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
rt[p->to] = T.add(rt[x], 1, N-1, p->clr, p->dis);
dis[p->to] = dis[x] + p->dis;
dfs(p->to, x);
}
}
int lca(int u, int v) {
if( dep[u] < dep[v] ) swap(u, v);
for(int i=19;i>=0;i--)
if( dep[fa[i][u]] >= dep[v] )
u = fa[i][u];
if( u == v ) return u;
for(int i=19;i>=0;i--)
if( fa[i][u] != fa[i][v] )
u = fa[i][u], v = fa[i][v];
return fa[0][u];
}
int main() {
scanf("%d%d", &N, &Q);
for(int i=1;i<N;i++) {
int a, b, c, d; scanf("%d%d%d%d", &a, &b, &c, &d);
addedge(a, b, c, d);
}
rt[1] = T.NIL, dfs(1, 0);
for(int i=1;i<=Q;i++) {
int x, y, u, v; scanf("%d%d%d%d", &x, &y, &u, &v);
int p = lca(u, v);
pii a = T.query(rt[u], 1, N-1, x), b = T.query(rt[v], 1, N-1, x), c = T.query(rt[p], 1, N-1, x);
printf("%d\n", (dis[u]+dis[v]-2*dis[p]) - (a.second+b.second-2*c.second) + (a.first+b.first-2*c.first)*y);
}
}

@details@

他们在说这道题很难。。。

我觉得是因为他们没学过莫队(还有可持久化)的原因吧。。。

话说 ABC 什么时候把难度加到这种程度了。。。我记得以前的 ABC不是全是简单模拟简单dp简单贪心嘛。。。

@atcoder - ABC133F@ Colorful Tree的更多相关文章

  1. ABC133F - Colorful Tree

    ABC133FColorful Tree 题意 给定一颗边有颜色和权值的树,多次询问,每次询问,首先更改颜色为x的边的权值为y,然后输出u到v的距离. 数据都是1e5量级的. 思路 我自己一开始用树链 ...

  2. AtCoder Beginner Contest 133 F Colorful Tree

    Colorful Tree 思路: 如果强制在线的化可以用树链剖分. 但这道题不强制在线,那么就可以将询问进行差分,最后dfs时再计算每个答案的修改值, 只要维护两个数组就可以了,分别表示根节点到当前 ...

  3. 2017 Multi-University Training Contest - Team 1 1003&&HDU 6035 Colorful Tree【树形dp】

    Colorful Tree Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)T ...

  4. hdu6035 Colorful Tree 树形dp 给定一棵树,每个节点有一个颜色值。定义每条路径的值为经过的节点的不同颜色数。求所有路径的值和。

    /** 题目:hdu6035 Colorful Tree 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6035 题意:给定一棵树,每个节点有一个颜色值.定 ...

  5. HDU 6035 - Colorful Tree | 2017 Multi-University Training Contest 1

    /* HDU 6035 - Colorful Tree [ DFS,分块 ] 题意: n个节点的树,每个节点有一种颜色(1~n),一条路径的权值是这条路上不同的颜色的数量,问所有路径(n*(n-1)/ ...

  6. [HDU6793] Tokitsukaze and Colorful Tree

    题目 又是一个条历新年,窗前的灼之花又盛开了. 时隔多年,现在只有这一棵树上盛开着残存的 \(n\) 朵灼之花了. 尽管如此,这些灼之 花仍散发出不同色彩的微弱的光芒. 灼之花的生命极为短暂,但它的花 ...

  7. AtCoder - 3913 XOR Tree

    Problem Statement You are given a tree with N vertices. The vertices are numbered 0 through N−1, and ...

  8. Colorful tree

    cnbb 我被数组清零卡了一天.. 子树改色询问子树颜色数.. 先考虑颜色为x的节点对祖先答案的贡献,那么我们考虑把所有这些节点都搞出来,按dfs序排序,然后考虑每个节点a掌管的祖先是它和按dfs序的 ...

  9. 2017ACM暑期多校联合训练 - Team 1 1003 HDU 6035 Colorful Tree (dfs)

    题目链接 Problem Description There is a tree with n nodes, each of which has a type of color represented ...

随机推荐

  1. php pdo操作数据库的方法

    PDO 安装 你可以通过 PHP 的 phpinfo() 函数来查看是否安装了PDO扩展. 1.在 Unix /linux系统上安装 PDO 在Unix上或Linux上你需要添加以下扩展: exten ...

  2. Django多业务模块的写法

    from django.shortcuts import render # Create your views here. from django.shortcuts import HttpRespo ...

  3. 微信小程序开发之图片等比例缩放 获取屏幕尺寸图片尺寸 自适应

    wxml: <image style="width: {{imagewidth}}px; height: {{imageheight}}px;"  src="{{i ...

  4. 服务端关闭session的重要性

    原因如下: 如果你的站点是一个每日请求上百万级规模的接口服务, 每一次请求(这里应该是没有携带cookie的请求)都会在服务端产生一个session会话,也就是一个小文件. 一般而言,php的sess ...

  5. 《2019上半年DDoS攻击态势报告》发布:应用层攻击形势依然严峻,海量移动设备成新一代肉鸡

    2019年上半年,阿里云安全团队平均每天帮助用户防御2500余次DDoS攻击,与2018年持平.目前阿里云承载着中国40%网站流量,为全球上百万客户提供基础安全防御.可以说,阿里云上的DDoS攻防态势 ...

  6. JavaScript--预解析在IE存在的问题

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  7. Wireshark 基本使用方法

    学习于: http://www.cnblogs.com/TankXiao/archive/2012/10/10/2711777.html

  8. Linux安装mongoDB步骤和方法

    Linux安装mongoDB步骤和方法 下载mongoDB数据库 mongodb-linux-x86_64-3.0.15.tgz 存放到linux文件夹中 ftp软件直接拖上去 解压文件夹(解压后,会 ...

  9. 两种RBAC权限控制模型详解

    序言 由于最近一直卡在权限控制这个坎上,原来设计的比较简单的权限控制思路已经无法满足比较复杂一些的场景,因此一直在探索一种在大部分场景下比较通用的权限模型. 首先,这里说明一下两种RBAC权限模型分别 ...

  10. jmeter的关联-正则表达式的应用

    LoadRunnner中的关联为web_reg_save_param,查找左右边界,下次请求的时候会用到上次请求服务器返回的数据,那么我们把符合左右边界的数据保存下来,以便下次请求的时候用到. jme ...