树链剖分模版

来源:互联网 发布:淘宝网能卖军用品 编辑:程序博客网 时间:2024/05/22 05:36

题目:spoj375

树链剖分训练题目

题意:给定一棵n节点的树,有两种操作,①修改某一条边的权值②查询某一条链u-->v的权值最大的边。

分析:树链剖分入门学习

对树链剖分的初步认识:

①把树上的边分为两类:重边轻边。

②任意两个节点u和v连接的这条链上,重链和轻边的数目都不超过logn(n为总的边数)

很明显,以某个轻节点为根的子树的规模不会超过父亲节点的规模的一半,否则就不是轻节点了。

1.在链u--->v上,轻边数目不超过logn

瞎证明:

lca=LCA(u,v)。

首先证明u-->lca的链上,轻边数目不超过logn。在极端情况下,链上的边全部为轻边。

由于u--->lca的这条链上以每个点为根节点的子树的大小最多为父亲节点的一半,从lca开始往下走,每次节点的数目都会最少减少一半,所以最多减少logn次就会走到底。

同理证明v--->lca的链上,轻边数目不超过logn。

所以,在链u--->v上,轻边数目不超过2*logn.....

2.在链u--->v上,重链的数目不超过logn

瞎证明:

u--->v这条链可以看成一条链有两种颜色(就是重边和轻边),重边为黑,轻边为白。//白边把黑边分成很多段

初始链全部为黑色,现在如何将链涂色,使得黑边的数目最多,明显就是让白色交错在里面,而白色的边最多logn条,所以黑色的链最多也是logn条。


任意两个节点u和v连接的这条链上,一条重链上重边在线段树里面的编号是连续的,因此可以在线段树里面O(logn)访问。(从u到top[u]这条重链)

任意两个节点u和v连接的这条链上,轻边在线段树里面的编号不一定连续。

⑤查询和修改的u-->v这条链的时间复杂度为logn*logn

代码:

#include <stdio.h>#include <string.h>#include <iostream>#include <algorithm>#include <vector>#include <queue>#include <set>#include <map>#include <string>#include <math.h>#include <stdlib.h>using namespace std;#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1 const int INF = 2147483647;const int MAXN = 10010;struct node{    int v,next;}List[MAXN*2];int head[MAXN],cnt;int top[MAXN];//top[v]表示v所在的重链的顶端节点int fa[MAXN]; //父亲节点int deep[MAXN];//深度int sz[MAXN];//sz[v]表示以v为根的子树的节点数int p[MAXN];//p[v]表示v与其父亲节点的连边在线段树中的位置int fp[MAXN];//和p数组相反int son[MAXN];//重儿子int pos;void init(){    cnt = 0;    memset(head,-1,sizeof(head));    pos = 0;    memset(son,-1,sizeof(son));memset(fa,-1,sizeof(fa));}void add(int u,int v){    List[cnt].v=v;    List[cnt].next=head[u];    head[u]=cnt++;}int dfs1(int cur,int dp)  //第一遍dfs求出fa,deep,sz,son{deep[cur]=dp;sz[cur]=1;son[cur]=-1;for(int i=head[cur];~i;i=List[i].next){int to=List[i].v;if(fa[cur]!=to){fa[to]=cur;sz[cur]+=dfs1(to,dp+1);if(son[cur]==-1 || sz[to]>sz[son[cur]])son[cur]=to; }}return sz[cur];}void dfs2(int cur,int sp)  //第二遍dfs求出top和p{top[cur]=sp;p[cur]=pos++;fp[p[cur]]=cur;if(son[cur]==-1)return ;dfs2(son[cur],sp);for(int i=head[cur];~i;i=List[i].next){int to=List[i].v;if(to!=son[cur] && fa[cur]!=to)dfs2(to,to);}}//线段树int tree[MAXN<<4];void update(int pos,int v,int l,int r,int rt){if(l==r){tree[rt]=v;return ;}int m=(l+r)>>1;if(pos<=m)update(pos,v,lson);elseupdate(pos,v,rson);tree[rt]=max(tree[rt<<1],tree[rt<<1|1]); }int query(int L,int R,int l,int r,int rt){if(L<=l && r<=R)return tree[rt];int m=(l+r)>>1,fg1=-INF,fg2=-INF;if(L<=m)fg1=query(L,R,lson);if(R>m)fg2=query(L,R,rson);return max(fg1,fg2);}int Q(int L,int R){return query(L,R,1,pos,1);}int find(int u,int v)//查询u->v边的最大值{    int f1 = top[u], f2 = top[v];    int tmp = 0;    while(f1 != f2)    {        if(deep[f1] < deep[f2])        {            swap(f1,f2);            swap(u,v);        }        tmp = max(tmp,Q(p[f1],p[u])); //f1在线段树里面的编号比u小         u = fa[f1]; f1 = top[u];    }    if(u == v)return tmp;    if(deep[u] > deep[v]) swap(u,v);    return max(tmp,Q(p[son[u]],p[v]));}int e[MAXN][3];int main(){    //freopen("in.txt","r",stdin);    //freopen("out.txt","w",stdout);    int T;    int n;    scanf("%d",&T);    while(T--)    {        init();        scanf("%d",&n);        for(int i = 0;i < n-1;i++)        {            scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]);            add(e[i][0],e[i][1]);            add(e[i][1],e[i][0]);        }        dfs1(1,1);        dfs2(1,1);        for(int i = 0;i < n-1; i++)        {            if(deep[e[i][0]] > deep[e[i][1]])                swap(e[i][0],e[i][1]);            update(p[e[i][1]],e[i][2],1,pos,1);        }        char op[10];        int u,v;        while(scanf("%s",op) == 1)        {            if(op[0] == 'D')break;            scanf("%d%d",&u,&v);            if(op[0] == 'Q')                printf("%d\n",find(u,v));            else update(p[e[u-1][1]],v,1,pos,1);        }    }    return 0;}


0 0
原创粉丝点击