树链剖分 FZU 2082

#include<cstring>

#include<cstdio>

#include<algorithm>

using namespace std;

const int maxn = 50005;

int val[maxn];

struct node

{

int l,r;

long long sum;

} xds[maxn<<2];

void build(int ID,int l,int r)

{

xds[ID].l=l;

xds[ID].r=r;

if(r==l)

{

xds[ID].sum=val[l];

return ;

}

int mid=(l+r)>>1;

build(ID<<1,l,mid);

build(ID<<1|1,mid+1,r);

xds[ID].sum=xds[ID<<1].sum+xds[ID<<1|1].sum;

}

long long query(int ID,int l,int r)

{

if(xds[ID].l==l&&xds[ID].r==r)

return xds[ID].sum;

int mid=(xds[ID].l+xds[ID].r)>>1;

if(r<=mid)

return query(ID<<1,l,r);

else if(l>mid)

return query(ID<<1|1,l,r);

else

return query(ID<<1,l,mid)+query(ID<<1|1,mid+1,r);

}

void gengxin(int ID,int l,int v)

{

if(xds[ID].l==l&&xds[ID].r==l)

{

xds[ID].sum=v;

return ;

}

int mid=(xds[ID].l+xds[ID].r)>>1;

if(l<=mid)

gengxin(ID<<1,l,v);

else

gengxin(ID<<1|1,l,v);

xds[ID].sum=xds[ID<<1].sum+xds[ID<<1|1].sum;

}

int id,ne,N,M;

int first[maxn],next[maxn<<1],top[maxn],idx[maxn],far[maxn],son[maxn],dep[maxn],cnt[maxn];

/*

first和next是邻接表数组,top存的是链的顶端节点,idx存的剖分后每个边在线段树中的位置,far【u】记录的是u节点的父亲节点

son[u]的重儿子节点,dep[u]记录的是u节点在树中的深度,cnt[u]记录的是u节点的所有节点(是所有节点哦)的个数包括自身节点的个数

*/

struct Edge//邻接表的存储结构体

{

int u,v,w;

void set(int u,int v,int w)

{

this->u=u;

this->v=v;

this->w=w;

}

} ed[maxn<<1];

void add_Edge(int u,int v,int w)//邻接表加边

{

ed[ne].set(u,v,w);

next[ne]=first[u];

first[u]=ne++;

}

void dfs(int u,int pre,int d)//递归求出,far,son,dep,cnt,u表示当前节点,pre表示u的父亲节点,d表示深度

{

far[u]=pre;

son[u]=0;

dep[u]=d;

cnt[u]=1;

for(int i=first[u]; i+1; i=next[i])//遍历以u为根节点的子节点

{

int v=ed[i].v;

if(v==far[u])//因为是无向图所以要把u的父亲节点排除外

continue;

dfs(v,u,d+1);

cnt[u]+=cnt[v];

if(cnt[son[u]]<cnt[v])//判断重儿子,也就是节点数最多的儿子

son[u]=v;

}

}

void dfs(int u,int root)//u代表当前节点,root代表根节点

{

idx[u]=++id;//让在同一重链上的点在线段树种相邻

top[u]=root;

if(son[u]) dfs(son[u],root);//如果u有重儿子就,先递归重儿子,因为重儿子和根节点的顶端节点相同,还有要让同一重链上的节点相邻

for(int i=first[u]; i+1; i=next[i])

{

int v=ed[i].v;

if(v==far[u]||v==son[u])//当v不是u父亲节点也不是u的重儿子时就是u的轻儿子

continue;

dfs(v,v);//轻儿子的顶端节点就是自身

}

}

void init()

{

int u,v,w;

id=ne=0;

memset(first,-1,sizeof(first));

for(int i=1; i<N; i++)

{

scanf("%d%d%d",&u,&v,&w);

add_Edge(u,v,w);

add_Edge(v,u,w);

}

dfs(1,0,0);

dfs(1,1);

for(int i=0; i<N-1; i++)//调整点来达到把边的花费存到深度较大的节点所在的线段树中的位置,比如说a-b-c相连用b存放a-b的花费,c存放b-c的花费

{

int t=i<<1;

if(dep[ed[t].u]<dep[ed[t].v])

swap(ed[t].u,ed[t].v);

val[idx[ed[t].u]]=ed[t].w;

}

build(1,1,id);//建树

}

long long slove(int u,int v)

{//轻链的是一条边一条边的计算,重链是是一条链的计算

int p=top[u],q=top[v];

long long ret=0;

while(p!=q)//p==q说明已经链的顶点已经是同一点

{

if(dep[p]<dep[q])//一直判断深浅的话就能很好的计算重链了,假如说一直从一点向另一点算的话就会就不能实现重链上一次性算完了,就如新浪博客上图的10-11的花费如果一直从10相11算的话树链剖分和不剖分就没有意义了

{

swap(p,q);

swap(u,v);

}

ret+=query(1,idx[p],idx[u]);

u=far[p];

p=top[u];

}

if(u==v))//说明是轻链

return ret;

if(dep[u]>dep[v])//说明是重链且不是根节点,在重链上求一段的花费

swap(u,v);

ret+=query(1,idx[son[u]],idx[v]);

return ret;

}

int main()

{

while(scanf("%d%d",&N,&M)!=EOF)

{

init();

for(int i=0; i<M; i++)

{

int k,u,v;

scanf("%d%d%d",&k,&u,&v);

if(k)

printf("%lld\n",slove(u,v));

else

gengxin(1,idx[ed[u*2-2].u],v);

}

}

return 0;

}

时间: 2024-11-01 21:38:34

树链剖分 FZU 2082的相关文章

FZU 2082 过路费 (树链剖分)边权

Problem 2082 过路费 Accept: 322    Submit: 1101 Time Limit: 1000 mSec    Memory Limit : 32768 KB Problem Description 有n座城市,由n-1条路相连通,使得任意两座城市之间可达.每条路有过路费,要交过路费才能通过.每条路的过路费经常会更新,现问你,当前情况下,从城市a到城市b最少要花多少过路费. Input 有多组样例,每组样例第一行输入两个正整数n,m(2 <= n<=50000,1&

FZU 2082 过路费 (树链剖分)

树链剖分裸题...不多说.. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.h> #include <map> #include <set> #include <stdio.h> using namespace

FZU 2082 树链剖分

点击打开链接 题意:中文 思路:最基础的树链剖分,区间求和以及单点更新,结果要用long long 就没什么了 #include <vector> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; ty

FZU2082 树链剖分(单点更新区间求值)

http://acm.fzu.edu.cn/problem.php?pid=2082  Problem Description 有n座城市,由n-1条路相连通,使得任意两座城市之间可达.每条路有过路费,要交过路费才能通过.每条路的过路费经常会更新,现问你,当前情况下,从城市a到城市b最少要花多少过路费.  Input 有多组样例,每组样例第一行输入两个正整数n,m(2 <= n<=50000,1<=m <= 50000),接下来n-1行,每行3个正整数a b c,(1 <=

FZU2176---easy problem (树链剖分)

http://acm.fzu.edu.cn/problem.php?pid=2176 Problem 2176 easy problem Accept: 9    Submit: 32Time Limit: 2000 mSec    Memory Limit : 32768 KB  Problem Description 给定一棵n个节点以1为根的树,初始每个节点的值为0,现在我们要在树上进行一些操作,操作有两种类型. 1 x val 表示对以x为根的子树的每个点进行加权操作(我们定义每个节点的

BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1886  Solved: 752[Submit][Status] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. In

bzoj 2243: [SDOI2011]染色 线段树区间合并+树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 7925  Solved: 2975[Submit][Status][Discuss] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完

bzoj3694: 最短路(树链剖分/并查集)

bzoj1576的帮我们跑好最短路版本23333(双倍经验!嘿嘿嘿 这题可以用树链剖分或并查集写.树链剖分非常显然,并查集的写法比较妙,涨了个姿势,原来并查集的路径压缩还能这么用... 首先对于不在最短路径树上的边x->y,设t为最短路径树上lca(x,y),则t到y上的路径上的点i到根的距离都可以用h[x]+dis[x][y]+h[y]-h[i](h[]为深度)来更新,因为h[i]一定,只要让h[x]+dis[x][y]+h[y]最小就行,这里用树剖直接修改整条链上的数,就可以过了. 并查集的

洛谷 P3384 【模板】树链剖分

题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和 操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z 操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和 输入输出格式 输入格式: 第一行包含4个正整数N.M.R.P,分别表示树的结点个数.操作个数