题目:http://acm.hdu.edu.cn/showproblem.php?pid=5044
题意:给定一个树,点和边都有权值,初始都为0。有t组数据,每组数据输入n和m,代表节点数和操作数,接下来n - 1行,每行两个数,代表两点有边相连。然后m行,形式如
ADD1 a b c 或 ADD2 a b c,前一种是把点a到点b路径上的点权都加上c,后一种是把点a到点b路径上的边权都加上c。最后输出每个点的点权和每条边的边权(边权按输入边的顺序输出)
思路:树链剖分,同时维护边权和点权。刚开始用的线段树lazy操作,超时了。。。然后百度一发,区间更新时用类似树状数组的方式,更为高效(并不知道这种方式具体叫什么,只是很常用就是了。。。),中间很无语的PE一发,就过了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
struct edge
{
int to, next;
}g[N*2];
struct node
{
int l, r, nval, eval, nmark, emark;
}s[N*4];
int head[N], siz[N], son[N], top[N], fat[N], dep[N], id[N];
int d[N][2], ecnt[N], ncnt[N];
int n, m, cnt, num, _case = 0;
void add_edge(int v, int u)
{
g[cnt].to = u;
g[cnt].next = head[v];
head[v] = cnt++;
}
void dfs1(int v, int d, int fa)
{
siz[v] = 1, son[v] = 0, fat[v] = fa, dep[v] = d;
for(int i = head[v]; i != -1; i = g[i].next)
{
int u = g[i].to;
if(u != fa)
{
dfs1(u, d + 1, v);
siz[v] += siz[u];
if(siz[son[v]] < siz[u]) son[v] = u;
}
}
}
void dfs2(int v, int tp)
{
top[v] = tp, id[v] = ++num;
if(son[v]) dfs2(son[v], tp);
for(int i = head[v]; i != -1; i = g[i].next)
{
int u = g[i].to;
if(u != fat[v] && u != son[v]) dfs2(u, u);
}
}
void update(int l, int r, int c, int q)
{
/*至今不知道这种姿势叫什么,虽然用过很多次*/
if(q == 1) ncnt[l] += c, ncnt[r+1] -= c;
else ecnt[l] += c, ecnt[r+1] -= c;
}
void renew(int v, int u, int c, int q)
{
int t1 = top[v], t2 = top[u];
while(t1 != t2)
{
if(dep[t1] < dep[t2])
swap(t1, t2), swap(v, u);
update(id[t1], id[v], c, q);
v = fat[t1], t1 = top[v];
}
if(dep[v] > dep[u]) swap(v, u);
if(q == 1) update(id[v], id[u], c, q);
else
{
if(u != v) update(id[son[v]], id[u], c, q);
}
}
void slove()
{
char str[10];
int a, b, c;
memset(ncnt, 0, sizeof ncnt);
memset(ecnt, 0, sizeof ecnt);
while(m--)
{
scanf(" %s%d%d%d", str, &a, &b, &c);
if(strcmp(str, "ADD1") == 0)
renew(a, b, c, 1);
else renew(a, b, c, 2);
}
printf("Case #%d:\n", ++_case);
for(int i = 1; i <= n; i++)
ncnt[i] += ncnt[i-1], ecnt[i] += ecnt[i-1];
for(int i = 1; i <= n; i++)
if(i != n) printf("%d ", ncnt[id[i]]);
else printf("%d\n", ncnt[id[i]]);
for(int i = 1; i <= n - 1; i++)
if(i != n - 1) printf("%d ", ecnt[id[d[i][1]]]);
else printf("%d\n", ecnt[id[d[i][1]]]);
if(n == 1) printf("\n"); /*点为1时没有边,但要输出一个空行,PE了一次。。。无语*/
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
memset(head, -1, sizeof head);
cnt = num = 0;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n -1; i++)
{
scanf("%d%d", &d[i][0], &d[i][1]);
add_edge(d[i][0], d[i][1]);
add_edge(d[i][1], d[i][0]);
}
dfs1(1, 1, 0);
dfs2(1, 1);
for(int i = 1; i <= n - 1; i++)
if(dep[d[i][0]] > dep[d[i][1]]) swap(d[i][0], d[i][1]);
slove();
}
return 0;
}
附线段树超时代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
struct edge
{
int to, next;
}g[N*2];
struct node
{
int l, r, nval, eval, nmark, emark;
}s[N*4];
int head[N], siz[N], son[N], top[N], fat[N], dep[N], id[N];
int d[N][2];
int n, m, cnt, num, _case = 0;
void add_edge(int v, int u)
{
g[cnt].to = u;
g[cnt].next = head[v];
head[v] = cnt++;
}
void dfs1(int v, int d, int fa)
{
siz[v] = 1, son[v] = 0, fat[v] = fa, dep[v] = d;
for(int i = head[v]; i != -1; i = g[i].next)
{
int u = g[i].to;
if(u != fa)
{
dfs1(u, d + 1, v);
siz[v] += siz[u];
if(siz[son[v]] < siz[u]) son[v] = u;
}
}
}
void dfs2(int v, int tp)
{
top[v] = tp, id[v] = ++num;
if(son[v]) dfs2(son[v], tp);
for(int i = head[v]; i != -1; i = g[i].next)
{
int u = g[i].to;
if(u != fat[v] && u != son[v]) dfs2(u, u);
}
}
void build(int l, int r, int k)
{
s[k].l = l, s[k].r = r, s[k].eval = 0, s[k].emark = 0, s[k].nval = 0, s[k].nmark = 0;
if(l == r) return;
int mid = (l + r) >> 1;
build(l, mid, k << 1);
build(mid + 1, r, k << 1|1);
}
void push_down(int k)
{
if(s[k].emark)
{
s[k<<1].eval += (s[k<<1].r - s[k<<1].l + 1) * s[k].emark;
s[k<<1|1].eval += (s[k<<1|1].r - s[k<<1|1].l + 1) * s[k].emark;
s[k<<1].emark += s[k].emark;
s[k<<1|1].emark += s[k].emark;
s[k].emark = 0;
}
if(s[k].nmark)
{
s[k<<1].nval += (s[k<<1].r - s[k<<1].l + 1) * s[k].nmark;
s[k<<1|1].nval += (s[k<<1|1].r - s[k<<1|1].l + 1) * s[k].nmark;
s[k<<1].nmark += s[k].nmark;
s[k<<1|1].nmark += s[k].nmark;
s[k].nmark = 0;
}
}
void update(int l, int r, int c, int q, int k)
{
if(l <= s[k].l && s[k].r <= r)
{
if(q == 1)
{
s[k].nval += (s[k].r - s[k].l + 1) * c;
s[k].nmark += c;
}
else
{
s[k].eval += (s[k].r - s[k].l + 1) * c;
s[k].emark += c;
}
return;
}
push_down(k);
int mid = (s[k].l + s[k].r) >> 1;
if(l <= mid) update(l, r, c, q, k << 1);
if(r > mid) update(l, r, c, q, k << 1|1);
}
int query(int x, int q, int k)
{
if(s[k].l == s[k].r)
{
if(q == 1) return s[k].nval;
else return s[k].eval;
}
push_down(k);
int mid = (s[k].l + s[k].r) >> 1;
if(x <= mid) return query(x, q, k << 1);
else return query(x, q, k << 1|1);
}
void renew(int v, int u, int c, int q)
{
int t1 = top[v], t2 = top[u];
while(t1 != t2)
{
if(dep[t1] < dep[t2])
swap(t1, t2), swap(v, u);
update(id[t1], id[v], c, q, 1);
v = fat[t1], t1 = top[v];
}
if(dep[v] > dep[u]) swap(v, u);
if(q == 1) update(id[v], id[u], c, q, 1);
else
{
if(u != v) update(id[son[v]], id[u], c, q, 1);
}
}
void slove()
{
char str[10];
int a, b, c;
while(m--)
{
scanf(" %s%d%d%d", str, &a, &b, &c);
if(strcmp(str, "ADD1") == 0)
renew(a, b, c, 1);
else renew(a, b, c, 2);
}
printf("Case #%d:\n", ++_case);
for(int i = 1; i <= n; i++)
if(i != n) printf("%d ", query(id[i], 1, 1));
else printf("%d\n", query(id[i], 1, 1));
for(int i = 1; i <= n - 1; i++)
if(i != n - 1) printf("%d ", query(id[d[i][1]], 2, 1));
else printf("%d\n", query(id[d[i][1]], 2, 1));
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
memset(head, -1, sizeof head);
cnt = num = 0;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n -1; i++)
{
scanf("%d%d", &d[i][0], &d[i][1]);
add_edge(d[i][0], d[i][1]);
add_edge(d[i][1], d[i][0]);
}
dfs1(1, 1, 0);
dfs2(1, 1);
for(int i = 1; i <= n - 1; i++)
if(dep[d[i][0]] > dep[d[i][1]]) swap(d[i][0], d[i][1]);
build(1, num, 1);
slove();
}
return 0;
}