HDU 5044 Tree:http://acm.hdu.edu.cn/showproblem.php?pid=5044
题意:给你一棵树,定义两种操作 1.将节点a,b路径上的每个节点都加上一个值k 2.将节点a,b路径上的每条边都加上一个值k
最后输出每个节点的权值和每条边的权值。
思路:此题目算是我树链剖分的第一题了,去kuangbin大神的博客上取的模板。如果是最朴素的操作每次操作的复杂度都是o(n),复杂度肯定是不够的,而树链剖分就可以将这个o(n)的复杂度降到o(logn),剩下的处理就是线性的处理了。
线性的处理方法就是打标记,以操作一为例,我们可以将节点a上打一个k的标记,在节点b+1上打一个-k的标记,当所有操作处理完之后从头到尾扫一遍即可求出最后的结果。
code:
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <cstring>
//树链剖分 很容易爆栈 此代码是C++的手动扩栈 G++可能不爆栈但是会T的
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int maxn=105000;
const int maxe=400010;
struct edge
{
int to,next;
} P[maxe];
int head[maxn],si;
int top[maxn];
int fa[maxn];
int deep[maxn];
int num[maxn];
int p[maxn];
int fp[maxn];
int son[maxn];
int pos;
typedef long long LL;
void init()
{
si=0;
memset(head,-1,sizeof(head));
pos=1;
memset(son,-1,sizeof(son));
}
void add_edge(int s,int t)
{
P[si].to=t;
P[si].next=head[s];
head[s]=si++;
}
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=P[i].next){
int v=P[i].to;
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 getpos(int u,int sp)
{
top[u]=sp;
p[u]=pos++;
fp[p[u]]=u;
if(son[u]==-1) return ;
getpos(son[u],sp);
for(int i=head[u];i!=-1;i=P[i].next){
int v=P[i].to;
if(v!=son[u]&&v!=fa[u]) getpos(v,v);
}
}
long long w1[maxn];
void add1(int u,int v,int w)
{
int f1=top[u],f2=top[v];
while(f1!=f2){
if(deep[f1]<deep[f2]){
swap(f1,f2);
swap(u,v);
}
w1[p[f1]]+=w;
w1[p[u]+1]-=w;
u=fa[f1];
f1=top[u];
}
if(deep[u]>deep[v]) swap(u,v);
w1[p[u]]+=w;
w1[p[v]+1]-=w;
}
long long w2[maxn];
void add2(int u,int v,int w){
int f1=top[u],f2=top[v];
while(f1!=f2){
if(deep[f1]<deep[f2]){
swap(f1,f2);
swap(u,v);
}
w2[p[f1]]+=w;
w2[p[u]+1]-=w;
u=fa[f1];
f1=top[u];
}
if(u==v) return ;
if(deep[u]>deep[v]) swap(u,v);
w2[p[son[u]]]+=w;
w2[p[v]+1]-=w;
}
pair<int,int> pp[maxn];
int link[maxn];
LL ans1[maxn],ans2[maxn];
int main()
{
int T,nn,mm,s,t,w;
char cc[10];
scanf("%d",&T);
for(int kk=1;kk<=T;kk++){
scanf("%d%d",&nn,&mm);
init();
for(int i=0;i<nn-1;i++){
scanf("%d%d",&s,&t);
add_edge(s,t);
add_edge(t,s);
pp[i]=make_pair(s,t);
}
dfs1(1,0,0);
getpos(1,1);
memset(w1,0,sizeof(w1));
memset(w2,0,sizeof(w2));
while(mm--){
scanf("%s",cc);
scanf("%d%d%d",&s,&t,&w);
if(cc[3]=='1') add1(s,t,w);
else add2(s,t,w);
}
//每条边都存在深度较大的那个节点上
for(int i=0;i<nn-1;i++){
if(deep[pp[i].first]>deep[pp[i].second]) swap(pp[i].first,pp[i].second);
link[pp[i].second]=i;
}
for(int i=1;i<=nn;i++){
w1[i]+=w1[i-1];
w2[i]+=w2[i-1];
ans1[fp[i]]=w1[i];
ans2[link[fp[i]]]=w2[i];
}
printf("Case #%d:\n",kk);
for(int i=1;i<=nn;i++){
printf("%I64d%c",ans1[i],i==nn? '\n':' ');
}
for(int i=0;i<nn-1;i++){
printf("%I64d",ans2[i]);
if(i!=nn-2) printf(" ");
}
printf("\n");
}
return 0;
}
这个算是我第一道树链剖分的题目了