题意:给出一个树。有两种操作,1:对节点到节点v上的路径的节点的值加K。2:对节点u到节点v上的路径的每条边的值加K。最后,把每条边和点的权值输出。
思路:看到路径就该想到树链剖分了吧。。。因为是离线的,我们可以直接打标记,然后离线算就可以了。
思考:第一次写树链剖分,思考了很长时间的点剖分和链剖分的区别。其实。两个区别是很小的。第二次dfs的时候,我们是对每个节点重新标号。所以,我们直接得到的是点的剖分。而链的剖分是由每条边的深度更大的点的标号来代替的。
注意:1.因为分别要对边和点进行操作,我们要分别统计。
2.因为点比较多,dfs会爆栈,需要手动扩栈或者用bfs.
3.最坑的就是在:整个图会有一个点,然后没有边,但是这样边仍然要输出一行。
代码如下:
#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int MAX = 100100;
struct edge{
int u,v;
edge(int uu, int vv):u(uu),v(vv){}
};
int T,N,Q;
int u,v,k;
int head[MAX],nxt[MAX<<2],to[MAX<<2],tot;
char str[5];
int fa[MAX],top[MAX],deep[MAX],num[MAX],p[MAX],fp[MAX],son[MAX];
int pos;
long long ans[MAX];
long long ans1[MAX];
void init()
{
E.clear();
tot = pos = 0;
memset(head,-1,sizeof(head));
memset(ans,0,sizeof(ans));
memset(ans1,0,sizeof(ans1));
memset(son,-1,sizeof(son));
}
void addedge(int u, int v)
{
to[tot] = v; nxt[tot] = head[u];
head[u] = tot++;
to[tot] = u; nxt[tot] = head[v];
head[v] = tot++;
}
void dfs1(int u,int pre,int d)
{
deep[u] = d;
fa[u] = pre;
num[u] = 1;
for(int i = head[u];i != -1; i = nxt[i])
{
int v = to[i];
if(v != pre)
{
dfs1(v,u,d+1);
num[u] += num[v];
if(son[u] == -1 || num[v] > num[son[u]])
son[u] = v;
}
}
}
void dfs2(int u, int sp)
{
top[u] = sp;
if(son[u] != -1){
p[u] = pos++;
fp[p[u]] = u;
dfs2(son[u],sp);
}
else{
p[u] = pos++;
fp[p[u]] = u;
return;
}
for(int i = head[u]; i != -1; i = nxt[i]){
int v = to[i];
if(v != son[u] && v != fa[u])
dfs2(v,v);
}
}
void update(int u, int v,int k)
{
int f1 = top[u], f2 = top[v];
while(f1 != f2){
if(deep[f1] < deep[f2]){
swap(f1,f2);
swap(u,v);
}
ans[p[f1]] += k, ans[p[u]+1] -= k;
u = fa[f1]; f1 = top[u];
}
if(u == v) return;
if(deep[u] > deep[v]) swap(u,v);
ans[p[son[u]]] += k, ans[p[v]+1] -= k;
}
void update1(int u, int v,int k)
{
int f1 = top[u], f2 = top[v];
while(f1 != f2){
if(deep[f1] < deep[f2]){
swap(f1,f2);
swap(u,v);
}
ans1[p[f1]] += k, ans1[p[u]+1] -= k;
u = fa[f1]; f1 = top[u];
}
if(u == v){
ans1[p[u]] += k, ans1[p[u]+1] -= k;
return ;
};
if(deep[u] > deep[v]) swap(u,v);
ans1[p[u]] += k, ans1[p[v]+1] -= k;
}
int main(void)
{
//freopen("input.txt","r",stdin);
//freopen("out.txt","w",stdout);
int cas = 1;
scanf("%d", &T);
while(T--){
init();
scanf("%d %d", &N, &Q);
for(int i = 0; i < N -1; ++i){
scanf("%d %d", &u,&v);
addedge(u,v);
}
dfs1(1,0,0);
dfs2(1,1);
for(int i = 0; i < Q; ++i){
scanf("%s %d %d %d",str,&u,&v,&k);
if(str[3] == '2'){
update(u,v,k);
}
else{
update1(u,v,k);
}
}
long long sum = 0;
for(int i = 0; i < pos; ++i){
sum += ans[i];
ans[i] = sum;
}
for(int i = 0; i < pos; ++i){
sum += ans1[i];
ans1[i] = sum;
}
printf("Case #%d:\n",cas++);
for(int i = 1; i <= N; ++i)
printf("%I64d%c",ans1[p[i]],i == N?'\n':' ');
for(int i = 0; i < tot-2; i += 2){
u = to[i+1];
v = to[i];
if(fa[u] == v)
printf("%I64d ",ans[p[u]]);
else
printf("%I64d ",ans[p[v]]);
}
if(tot != 0){
u = to[tot-2];v = to[tot-1];
if(fa[u] == v) printf("%I64d\n",ans[p[u]]);
else printf("%I64d\n",ans[p[v]]);
}
else puts("");
}
return 0;
}