0%

HDU 5044 Tree 树链剖分

Tree

Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)

Problem Description

You are given a tree (an acyclic undirected connected graph) with N nodes. The tree nodes are numbered from 1 to N

There are N - 1 edges numbered from 1 to N - 1.

Each node has a value and each edge has a value. The initial value is 0.

There are two kind of operation as follows:

  • ADD1 u v k: for nodes on the path from u to v, the value of these nodes increase by k.
  • ADD2 u v k: for edges on the path from u to v, the value of these edges increase by k.

After finished M operation on the tree, please output the value of each node and edge.

Input

The first line of the input is T (1 ≤ T ≤ 20), which stands for the number of test cases you need to solve.

The first line of each case contains two integers N ,M (1 ≤ N, M ≤105),denoting the number of nodes and operations, respectively.

The next N - 1 lines, each lines contains two integers u, v(1 ≤ u, v ≤ N ), denote there is an edge between u,v and its initial value is 0.

For the next M line, contain instructions “ADD1 u v k” or “ADD2 u v k”. (1 ≤ u, v ≤ N, -105 ≤ k ≤ 105)

Output

For each test case, print a line “Case #t:”(without quotes, t means the index of the test case) at the beginning.

The second line contains N integer which means the value of each node.

The third line contains N - 1 integer which means the value of each edge according to the input order.

Sample Input

2
4 2
1 2
2 3
2 4
ADD1 1 4 1
ADD2 3 4 2
4 2
1 2
2 3
1 4
ADD1 1 4 5
ADD2 3 2 4

Sample Output

Case #1:
1 1 0 1
0 2 2
Case #2:
5 0 0 5
0 4 0

题意

维护一棵树,操作是对某两点路径上的所有点的权值加上某个值,或者对某两点路径上的所有边的权值加上某个值。

分析

Kuang神出题的时候特别为这题卡了数据,LCT目测是不够过的,用树链剖分转成线性加输入挂也是勉强过,4700ms左右,那种1000~2000ms左右的算法不太明白是怎么写的。

1.维护两点路径上的点权值是树链剖分的基本操作

2.维护两点路径上的边权值需要在剖分的时候直接按照边的情况,直接用子结点的序号来代表一条边,然后做好边原本序号的记录

一般的树链剖分是直接用树状数组,但是由于本题的最终结果要求的是输出所有点和所有边的权值,因此采用树状数组每次求一次sum来得到每个点的值就有点浪费效率了。

可以参考原本树状数组的方式,直接用一个普通的数组来记录情况,在起点+value,在终点后的一个点-value,表示对整段线段完成加权值;最终求和的时候只需要从头向后扫一遍,求出每个点的sum(i)即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/* ***********************************************
MYID : Chen Fan
LANG : G++
PROG : HDU 5044
************************************************ */

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

#define MAXN (int)1E5+10
#define MAXM (int)2E5+10

int son[MAXN],ans[MAXN];
int father[MAXN],size[MAXN],level[MAXN],data[MAXN],top[MAXN];
int start[MAXN];

typedef struct nod
{
int to,next,no;
} node;
node edge[MAXM];
int last,n;

void addedge(int from,int to,int k)
{
last++;
edge[last].to=to;
edge[last].next=start[from];
edge[last].no=k;
start[from]=last;
}

int c[MAXN],pos[MAXN],fp[MAXN];
int ec[MAXN],epos[MAXN],fep[MAXN];
int tot;

int q[MAXN];
void bfs()
{
int head=1,tail=1;
q[1]=1;
level[1]=0;
father[1]=0;

while(head<=tail)
{
int now=q[head];
size[now]=1;
for (int i=start[now];i;i=edge[i].next)
{
int temp=edge[i].to;
if (temp!=father[now])
{
father[temp]=now;
fep[temp]=edge[i].no;
level[temp]=level[now]+1;
tail++;
q[tail]=temp;
}
}
head++;
}
for (int i=n;i>=1;i--)
{
int now=q[i];
if (father[now])
{
size[father[now]]+=size[now];
if (son[father[now]]==0||size[now]>size[son[father[now]]])
son[father[now]]=now;
}
}
for (int i=1;i<=n;i++)
{
int now=q[i];
if (son[father[now]]==now) top[now]=top[father[now]];
else
{
top[now]=now;
while(now)
{
tot++;
pos[now]=tot;
fp[tot]=now;
now=son[now];
}
}
}
}

void change(int x,int y,int value)
{
while(top[x]!=top[y])
{
if (level[top[x]]<level[top[y]]) swap(x,y);

c[pos[top[x]]]+=value;
c[pos[x]+1]-=value;

x=father[top[x]];
}
if (level[x]>level[y]) swap(x,y);

c[pos[x]]+=value;
c[pos[y]+1]-=value;
}

void change_edge(int x,int y,int value)
{
while(top[x]!=top[y])
{
if (level[top[x]]<level[top[y]]) swap(x,y);

ec[pos[top[x]]]+=value;
ec[pos[x]+1]-=value;

x=father[top[x]];
}
if (level[x]>level[y]) swap(x,y);

ec[pos[son[x]]]+=value;
ec[pos[y]+1]-=value;
}

int INT() {
char ch;
int res;
bool neg;
while (ch = getchar(), !isdigit(ch) && ch != '-')
;
if (ch == '-') {
neg = true;
res = 0;
} else {
neg = false;
res = ch - '0';
}
while (ch = getchar(), isdigit(ch))
res = res * 10 + ch - '0';
return neg ? -res : res;
}

int main()
{
freopen("5044.txt","r",stdin);

int t;
t=INT();

for (int tt=1;tt<=t;tt++)
{
printf("Case #%d:\n",tt);

int m;
n=INT();m=INT();

memset(start,0,sizeof(start));
last=0;
for (int i=1;i<n;i++)
{
int u,v;
u=INT();
v=INT();
addedge(u,v,i);
addedge(v,u,i);
}

memset(son,0,sizeof(son));
tot=0;
bfs();

memset(c,0,sizeof(c));
memset(ec,0,sizeof(ec));

for (int i=1;i<=m;i++)
{
char s;
int c1,c2,k;
s=getchar();
while (!isdigit(s)) s=getchar();
c1=INT();
c2=INT();
k=INT();
if (s=='1') change(c1,c2,k);
else change_edge(c1,c2,k);
}

int ss=0;
for (int i=1;i<=n;i++)
{
ss+=c[i];
ans[fp[i]]=ss;
}
printf("%d",ans[1]);
for (int i=2;i<=n;i++)
{
putchar(' ');
printf("%d",ans[i]);
}
putchar('\n');

ss=0;
for (int i=1;i<=n;i++)
{
ss+=ec[i];
ans[fep[fp[i]]]=ss;
}
if (n>1) printf("%d",ans[1]);
for (int i=2;i<n;i++)
{
putchar(' ');
printf("%d",ans[i]);
}
putchar('\n');

}

return 0;
}