@atcoder - ABC133F@ Colorful Tree
@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的更多相关文章
- ABC133F - Colorful Tree
ABC133FColorful Tree 题意 给定一颗边有颜色和权值的树,多次询问,每次询问,首先更改颜色为x的边的权值为y,然后输出u到v的距离. 数据都是1e5量级的. 思路 我自己一开始用树链 ...
- AtCoder Beginner Contest 133 F Colorful Tree
Colorful Tree 思路: 如果强制在线的化可以用树链剖分. 但这道题不强制在线,那么就可以将询问进行差分,最后dfs时再计算每个答案的修改值, 只要维护两个数组就可以了,分别表示根节点到当前 ...
- 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 ...
- hdu6035 Colorful Tree 树形dp 给定一棵树,每个节点有一个颜色值。定义每条路径的值为经过的节点的不同颜色数。求所有路径的值和。
/** 题目:hdu6035 Colorful Tree 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6035 题意:给定一棵树,每个节点有一个颜色值.定 ...
- HDU 6035 - Colorful Tree | 2017 Multi-University Training Contest 1
/* HDU 6035 - Colorful Tree [ DFS,分块 ] 题意: n个节点的树,每个节点有一种颜色(1~n),一条路径的权值是这条路上不同的颜色的数量,问所有路径(n*(n-1)/ ...
- [HDU6793] Tokitsukaze and Colorful Tree
题目 又是一个条历新年,窗前的灼之花又盛开了. 时隔多年,现在只有这一棵树上盛开着残存的 \(n\) 朵灼之花了. 尽管如此,这些灼之 花仍散发出不同色彩的微弱的光芒. 灼之花的生命极为短暂,但它的花 ...
- AtCoder - 3913 XOR Tree
Problem Statement You are given a tree with N vertices. The vertices are numbered 0 through N−1, and ...
- Colorful tree
cnbb 我被数组清零卡了一天.. 子树改色询问子树颜色数.. 先考虑颜色为x的节点对祖先答案的贡献,那么我们考虑把所有这些节点都搞出来,按dfs序排序,然后考虑每个节点a掌管的祖先是它和按dfs序的 ...
- 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 ...
随机推荐
- java项目小手册
集合了一些常用的小片段 1. 字符串有整型的相互转换 Java代码 String a = String.valueOf(2); //integer to numeric string int i = ...
- python实例 条件和循环语句
#! /usr/bin/python #条件和循环语句 x=int(input("Please enter an integer:")) if x<0: x=0 ...
- WordPress资料收集,以后整理
WordPress主题开发:实现分页功能 http://www.cnblogs.com/tinyphp/p/6361901.html WordPress如何调取显示指定文章 https://www.d ...
- Django用户登陆以及跳转后台管理页面3
Django用户登陆以及跳转后台管理页面1http://www.cnblogs.com/ujq3/p/7891774.html Django用户登陆以及跳转后台管理页面2http://www.cnbl ...
- HR招聘_(十)_招聘方法论(供应商管理)
招聘和供应商长期合作,所以供应商管理也至关重要.供应商一般分为猎头,渠道,外包三类. 猎头 高端职位,高难度职位,急需职位和量大职位会和猎头公司合作共同完成招聘任务,猎头公司一般会有两种服务,猎头和R ...
- awk notes
字符串拼接 cat sql | awk '{print " ALTER TABLE tableA ALTER " $1 " TYPE " $2 " ...
- DirectX11笔记(六)--Direct3D渲染2--VERTEX BUFFER
原文:DirectX11笔记(六)--Direct3D渲染2--VERTEX BUFFER 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u0103 ...
- 【水滴石穿】react-native-video-project
感觉这个是很有才华的博主,毕竟是可以在npm 包里面留后门的程序员 博主的gihtub关于这个项目的地址是:https://github.com/ikimiler/react-native-video ...
- Oracle存储过程小解
Oracle存储过程小解 1.创建语法 create or replace procedure pro_name( paramIn in type, paramOUt out type, paramI ...
- PHP:测试SQL注入以及防止SQL注入
在写登录注册的时候发现了SQL和JS注入这个危害网站的用户举动: 测试方法: SQL注入: 先来做一个测试: 用户名:’ or 1 # 密码:随便写8位以上 验证码:写正确 好吧,就那么简单就进去了: ...