题意:给一个简单图,在图上进行n此操作,最后按顺序输出点权和边权的值。T组数据,每组数据第一行两个数n,m分别代表树的点数,操作的次数,接下来n-1行每行两个数表示一条边。接下来m行表示m次操作,每次操作有一一个字符串add1,或add2,接下来是三个数u,v,c。add1表示在修改点权u->v的路径上的点加c,add2表示修改边权u->v的路径上的边权加c。
解题思路:知道题虽然是比较经典的树链剖分,但是并不是那么简单,首先它既要修改边权,又要修改点权这就非常考验对树链剖分的熟练度了,然后就是坑人的,不能用线段树去维护剖分出来的链,这么做会TLE。这里也是学到了一种非常巧妙的算法。用一个sum数组来进行标记进行的区间跟新,感觉跟扫描线的思想有点相似,最后操作完之后用另一个数组从头到尾扫一遍就好了。还是直接看代码,代码还是比较直观的。
#include <set>
#include <map>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <vector>
#include <iomanip>
#include <bitset>
#include <cstring>
#include <iostream>
#include <iosfwd>
#include <deque>
#include <algorithm>
#define Memset(a,val) memset(a,val,sizeof(a))
#define PI acos(-1.0)
#define PB push_back
#define MP make_pair
#define rt(n) (i == n ? '\n' : ' ')
#define hi printf("Hi----------\n")
#define IN freopen("input.txt","r",stdin);
#define OUT freopen("output.txt","w",stdout);
#define debug(x) cout<<"Debug : ---"<<x<<"---"<<endl;
#define debug2(x,y) cout<<"Debug : ---"<<x<<" , "<<y<<"---"<<endl;
#pragma comment(linker, "/STACK:1024000000,1024000000")
///有时数据范围较大,代码中两个DFS可能需要手动扩栈
using namespace std;
typedef pair<int,int> PII;
typedef long long ll;
const int maxn=100000+5;
const int mod=1000000007;
const int INF=0x3f3f3f3f;
const double eps=1e-8;
const int Vmax = 1*1e5 + 5;//点的数量
const int Emax =2*1e5+5;//边的数量 小于Vmax的两倍
namespace poufen{
int siz[Vmax],son[Vmax],fa[Vmax],dep[Vmax],top[Vmax],w[Vmax];
int nodenum;
int sum1[Vmax],sum2[Vmax];
///邻接表存图
struct edge{
int v,next;
}e[Emax];
int pre[Vmax],ecnt;
inline void init(){
memset(pre, -1, sizeof(pre));
ecnt=0;
}
inline void add_(int u,int v){
e[ecnt].v=v;
e[ecnt].next=pre[u];
pre[u]=ecnt++;
}
inline void update1(int u,int v,int c)///更新点权
{
sum1[u]+=c;
sum1[v+1]-=c;
}
inline void update2(int u,int v,int c)///更新边权
{
sum2[u]+=c;
sum2[v+1]-=c;
}
void dfs(int u){///dfs跑出siz、son、fa、dep的值
siz[u]=1;son[u]=0;//下标从1开始,son[0]初始为0
for(int i=pre[u];~i;i=e[i].next)
{
int v=e[i].v;
if(fa[u]!=v)
{
fa[v]=u;
dep[v]=dep[u]+1;//初始根节点dep!!
dfs(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
}
void build_tree(int u,int tp){///递归建树,记录top、w的值
top[u]=tp,w[u]=++nodenum;
if(son[u])build_tree(son[u],tp);
for(int i=pre[u];~i;i=e[i].next)
if(e[i].v!=fa[u]&&e[i].v!=son[u])
build_tree(e[i].v,e[i].v);
}
void upd1(int u,int v,int c)///更新点权
{
int f1=top[u],f2=top[v];
while(f1!=f2)
{
if(dep[f1]<dep[f2])
swap(f1,f2),swap(u,v);
update1(w[f1],w[u],c);
u=fa[f1];
f1=top[u];
}
if(dep[u]>dep[v])
swap(u,v);
update1(w[u],w[v],c);
}
void upd2(int u,int v,int c)///更新边权
{
int f1=top[u],f2=top[v];
while(f1!=f2)
{
if(dep[f1]<dep[f2])
swap(f1,f2),swap(u,v);
update2(w[f1],w[u],c);
u=fa[f1];
f1=top[u];
}
if(u==v)
return ;
if(dep[u]>dep[v])
swap(u,v);
update2(w[son[u]],w[v],c);
///注意这里是son[u]因为最终根节点对应的边权不进行更新
}
int a[Vmax],b[Vmax];
ll ans1[Vmax],ans2[Vmax];
int val[Vmax];
}
using namespace poufen;
int main()
{
int T,cas=1;
scanf("%d",&T);
while(T--)
{
int n,m;
printf("Case #%d:\n",cas++);
scanf("%d%d",&n,&m);
memset(siz, 0, sizeof(siz));
memset(sum1, 0, sizeof(sum1));
memset(sum2,0,sizeof(sum2));
init();
int root=1;
fa[root]=nodenum=dep[root]=0;
for(int i=1;i<n;i++)
{
scanf("%d%d",&a[i],&b[i]);
add_(a[i],b[i]);
add_(b[i],a[i]);
}
dfs(root);
build_tree(root,root);
char op[10];
int u,v,c;
for(int i=1;i<=m;i++)
{
scanf("%s%d%d%d",op,&u,&v,&c);
if(op[3]=='1')
upd1(u,v,c);
else
upd2(u,v,c);
}
for(int i=1;i<=n;i++)
{
ans1[i]=ans1[i-1]+sum1[i];
ans2[i]=ans2[i-1]+sum2[i];
}
printf("%I64d",ans1[w[1]]);
for(int i=2;i<=n;i++)
{
printf(" %I64d",ans1[w[i]]);
}
printf("\n");
for(int i=1;i<n;i++)
{
int u=a[i],v=b[i];
if(dep[u]>dep[v]) swap(u,v);
printf("%I64d",ans2[w[v]]);
if(i!=n-1)
printf(" ");
}
printf("\n");
}
return 0;
}