您的位置:首页 > 其它

[树链剖分]专题,学习记录

2015-10-19 21:58 176 查看
Maya,居然已经好久没写题解了……………………我的郭,今天上午考试没空写,昨天在刷水题找快感……

树链剖分早就想学了,今天终于下定了决心学了链剖,觉得自己萌萌搭,最然队友都说NOIP不考,好吧,就算不考,我就当练代码能力了好吧~

树链剖分个人开始也是翻了百度排名比较靠前的几个博客,但是理解起来还是很麻烦……,最后是直接拿SPOJ 375 QTREE这个题开刀打了一遍,才学会的……

如果你觉得链剖很难理解,那么我先建议你看这个博客:链剖图解+文字解读

理解了上面的那个博客中的想法之后,你可以看这个神犇写的代码~

链剖,树链剖分,把树上的边分成重边和轻边(我不喜欢叫重链或者轻链)。

链剖我们就是把平时暴力修改LCA的操作进行优化,优化到Log级别。它的具体操作就是把边映射到线段树中,把对边的修改改成对区间的修改,因为线段树的性质,所以就能把复杂度降低到log。

这样的具体操作就是在每次修改时改为直接访问对应在线段树中对应的编号,然后对线段树中对应区间修改,但是这样需要预处理一些东西,这些都在上面的博客中讲到了,这里不做具体讲解……

两个dfs的作用也都在上面博客中说了,我把我认为重要的部分加在代码注视中,并贴上3次打的不同的错误的代码(错误在注释中),希望大家可以从中找到容易出错的地方,加强练习 233333333

题目链接:Vjudge上面的

SPOJ上的

第一版~(调了一下午+半晚上,第一次A的时候那真是excited):

[code]#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<deque>
#include<algorithm>
using namespace std;
typedef long long ll;
const int INF = 2 << 28;
const int size = 100100;
//-----------------------------------建边部分-------------------------  
struct Edge{int to;}edges[size<<2];
int head[size],next[size],tot;
void build(int f,int t)
{
    edges[++tot].to = t;
    next[tot] = head[f];
    head[f] = tot;
}
//-----------------------------------树链部分-------------------------  
//记siz[v]表示以v为根的子树的节点数,dep[v]表示v的深度(根深度为1)  
//top[v]表示v所在的链的顶端节点,fa[v]表示v的父亲  
//son[v]表示与v在同一重链上的v的儿子节点(姑且称为重儿子)  
//w[v]表示v与其父亲节点的连边(姑且称为v的父边)在线段树中的位置
int son[size],fa[size],deep[size],top[size],siz[size],w[size];

void dfs_find(int u,int faa,int dep)
{
    son[u] = 0,fa[u] = faa,deep[u] = dep,siz[u] = 1;
    for(int i = head[u];i;i = next[i])
    {
        int v = edges[i].to;
        if(v == faa)    continue;
        dfs_find(v,u,dep+1);
        siz[u] += siz[v];
        if(siz[v] > siz[son[u]])    son[u] = v;
    }
}
int dfs_clock;
void dfs_time(int u,int faa)
{
    w[u] = ++dfs_clock; top[u] = faa;
    if(son[u])  dfs_time(son[u],faa);
    for(int i = head[u];i;i = next[i])
    {
        int v = edges[i].to;
        if(v != son[u] && v != fa[u])   dfs_time(v,v);
    }
}
//-----------------------------------线段树部分-------------------------  
struct Tree{
    int l,r,maxx;
}tree[size<<2];

void build(int p,int l,int r)   //作用:建树 
{
    tree[p].l = l,tree[p].r = r;
    tree[p].maxx = -INF;
    if(l == r)  return ;
    int mid = (l+r) >> 1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
}

void change(int p,int id,int w) //作用,处理max
{
    if(tree[p].l == tree[p].r)
    {
        tree[p].maxx = w;
        return ;
    }
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(id <= mid)   change(p<<1,id,w);
    else            change(p<<1|1,id,w);
    tree[p].maxx = max(tree[p<<1].maxx,tree[p<<1|1].maxx);
}

int query(int p,int l,int r)    //询问maxx 范围l~r
{
    if(l <= tree[p].l && tree[p].r <= r)    return tree[p].maxx;
    int mid = (tree[p].l + tree[p].r) >> 1;
    int ans = -INF;
    if(l <= mid)    ans = max(ans,query(p<<1,l,r)); //WTF
    if(mid < r)     ans = max(ans,query(p<<1|1,l,r));   //WTF
    return ans;
}

int ask(int x,int y)    //实际的点x,y,不是映射后的点 
{
    int ans = -INF;
    while(top[x] != top[y])
    {
        if(deep[top[x]] < deep[top[y]]) swap(x,y);
        ans = max(ans,query(1,w[top[x]],w[x])); //w[top[x]],w[x];
        x = fa[top[x]];
    }
    if(deep[x] > deep[y])   swap(x,y);
    if(x != y)
    {
        ans = max(ans,query(1,w[x]+1,w[y]));    //点权表示的上边的边的编号,指定修改的边对应的点是下面的点,此时应该传进去对应下面的点Orz 
    }
    return ans;
}
int edg[size][3];
void init()
{
    memset(edges,0,sizeof(edges));
    memset(edg,0,sizeof(edg));
    memset(head,0,sizeof(head));
    memset(next,0,sizeof(next));
    memset(tree,0,sizeof(tree));
    tot = 0;
    dfs_clock = 0;
}
int n;
char in[233];
int main()
{
//  ios::sync_with_stdio(0);
    int t;
    scanf("%d",&t);
//  cin>>t;
    while(t --)
    {
        scanf("%d",&n);
//      cin>>n;
        init();
        for(int i = 1;i < n;i ++)
        {
            scanf("%d%d%d",&edg[i][0],&edg[i][1],&edg[i][2]);
//          cin>>edg[i][0]>>edg[i][1]>>edg[i][2];
            build(edg[i][0],edg[i][1]);
            build(edg[i][1],edg[i][0]);
        }
        dfs_find(1,1,1);
        dfs_time(1,1);
/*      cout<<endl;
        for(int i = 1;i <= n;i ++)  cout<<w[i]<<endl;
        cout<<endl;*/
        build(1,1,n);
/*      cout<<endl;
        for(int i = 1;i <= n;i ++)  cout<<w[i]<<endl;
        cout<<endl;*/
        for(int i = 1;i < n;i ++)
        {
            if(deep[edg[i][0]] > deep[edg[i][1]])   swap(edg[i][0],edg[i][1]);//大于号 
            change(1,w[edg[i][1]],edg[i][2]);//错误写法:change(1,edg[i][0],edg[i][2]);
        }
/*      cout<<endl;
        for(int i = 1;i < n;i ++)
        {
            cout<<w[edg[i][1]]<<" "<<edg[i][2]<<endl;
        }
        cout<<endl;*/
        while(233)
        {
            scanf("%s",in);
            if(in[0] == 'D')    break;
            if(in[0] == 'Q')
            {
                int a,b;
                scanf("%d%d",&a,&b);
                printf("%d\n",ask(a,b));
//              cin>>a>>b;
//              cout<<ask(a,b)<<endl;
            }
            else
            {
                int a,b;
                scanf("%d%d",&a,&b);
//              cin>>a>>b;
                change(1,w[edg[a][1]],b);
            }
        }
        puts("");
    }
    return 0;
}


第二版:

[code]#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<deque>
#include<algorithm>
using namespace std;
typedef long long ll;
const int INF = 2 << 28;
const int size = 100010;
//建边 
struct Edge{int to;}edges[size<<1];
int head[size],next[size<<1],tot;
void build(int f,int t)
{
    edges[++tot].to = t;
    next[tot] = head[f];
    head[f] = tot;
}
//链剖-----------------------------------//
int son[size],siz[size],deep[size],top[size],fa[size],w[size];
void dfs(int u,int faa,int dep)
{
    siz[u] = 1,fa[u] = faa,son[u] = 0,deep[u] = dep;
    for(int i = head[u];i;i = next[i])
    {
        int v = edges[i].to;
        if(v == faa)    continue;   //这里一定要写==的条件,如果写!=执行比较麻烦而且容易写错记错,保险起见还是这么写 
        dfs(v,u,dep+1);
        siz[u] += siz[v];
        if(siz[v] > siz[son[u]])    son[u] = v;
    }
}

int dfs_clock;
void dfs2(int u,int faa)
{
    w[u] = ++dfs_clock; top[u] = faa;
    if(son[u])  dfs2(son[u],faa);
    for(int i = head[u];i;i = next[i])
    {
        int v = edges[i].to;
        if(v != son[u] && v != fa[u])   dfs2(v,v);
    }
}
//END------------------------------------//
//线段树部分
struct Tree{
    int l,r,maxx;
}tree[size<<2];

void build(int p,int l,int r)
{
    tree[p].l = l,tree[p].r = r,tree[p].maxx = -INF;
    if(l == r)  return ;
    int mid = (l + r) >> 1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
}

void change(int p,int id,int w)
{
    if(tree[p].l == tree[p].r)
    {
        tree[p].maxx = w;
        return ;
    }
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(id <= mid)   change(p<<1,id,w);  //不是 tree[p].l,因为要找id的位置,tree[p].l一定小于mid;自行脑补 
    else            change(p<<1|1,id,w);
    tree[p].maxx = max(tree[p<<1].maxx,tree[p<<1|1].maxx);  //update啊别忘了!!!
}

int query(int p,int l,int r)
{   //这里是查询啊外,没有return怎么算查询 
    if(l <= tree[p].l && tree[p].r <= r)    return tree[p].maxx;
    int mid = (tree[p].l + tree[p].r) >> 1;
    int ans = -INF;
    if(l <= mid)    ans = max(ans,query(p<<1,l,r));
    if(mid < r)     ans = max(ans,query(p<<1|1,l,r));
    return ans;
}

int ask(int l,int r)
{
    int ans = - INF;
    while(top[l] != top[r])
    {
        if(deep[top[l]] < deep[top[r]]) swap(l,r);  //放到里面?保证每次都是满足deep[top[l]] > deep[top[r]] 
        ans = max(ans,query(1,w[top[l]],w[l]));
        l = fa[top[l]];
    }
    if(deep[l] > deep[r])   swap(l,r);
    if(l != r)
    {
        ans = max(ans,query(1,w[l]+1,w[r]));//点权表示的上边的边的编号,指定修改的边对应的点是下面的点,此时应该传进去对应下面的点Orz 
    }
    return ans;
}

int ed[size][3];
void init()
{
    tot = dfs_clock = 0;
    memset(edges,0,sizeof(edges));
    memset(head,0,sizeof(head));
    memset(next,0,sizeof(next));
    memset(tree,0,sizeof(tree));    
}
int n;
char dora[450];
int main()
{
    int t;
    scanf("%d",&t);
    while(t --)
    {
        init();
        scanf("%d",&n);
        for(int i = 1;i < n;i ++)
        {
            scanf("%d%d%d",&ed[i][0],&ed[i][1],&ed[i][2]);
            build(ed[i][0],ed[i][1]);
            build(ed[i][1],ed[i][0]);
        }
        dfs(1,1,1);
        dfs2(1,1);
        build(1,1,n);
        //写了函数不用!!!操蛋那! 
        for(int i = 1;i < n;i ++)
        {
            if(deep[ed[i][0]] > deep[ed[i][1]])/*这里写deep是要保证左节点一定是在上面的,并不是比较编号!*/   swap(ed[i][0],ed[i][1]);
            change(1,w[ed[i][1]],ed[i][2]);
        }
        while(233)
        {
            scanf("%s",dora);
            if(dora[0] == 'D')  break;
            if(dora[0] == 'Q')
            {
                int a,b;
                scanf("%d%d",&a,&b);
                printf("%d\n",ask(a,b));
            }
            else
            {
                int a,b;
                scanf("%d%d",&a,&b);
                change(1,w[ed[a][1]],b);
            }
        }
        puts("");
    }
    return 0;
}
/*
1

3
1 2 1
2 3 2
QUERY 1 2
CHANGE 1 3
QUERY 1 2
DONE
*/


第三版:

[code]#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<deque>
#include<algorithm>
using namespace std;
const int size = 100010;
const int INF = 2 << 28;
//建边
struct Edge{int to;}edges[size<<1]; 
int head[size],next[size<<1],tot;
void build(int f,int t)
{
    edges[++tot].to = t;
    next[tot] = head[f];
    head[f] = tot;
}
//链剖
int top[size],son[size],siz[size],deep[size],w[size],fa[size];
void dfs(int u,int faa,int dep)
{
    son[u] = 0,fa[u] = faa,deep[u] = dep,siz[u] = 1;
    for(int i = head[u];i;i = next[i])  //手癌晚期!!!i 写成tot 这还能玩? 
    {
        int v = edges[i].to;
        if(v == faa)    continue;
        dfs(v,u,dep+1);
        siz[u] += siz[v];
        if(siz[v] > siz[son[u]])    son[u] = v;
    }
}
int dfs_clock;
void dfs2(int u,int faa)
{
    w[u] = ++dfs_clock; top[u] = faa;
    if(son[u])  dfs2(son[u],faa);
    for(int i = head[u];i;i = next[i])
    {
        int v = edges[i].to;
        if(v != son[u] && v != fa[u])   dfs2(v,v);
    }
}
//线段树
struct Tree{
    int l,r,maxx;
}tree[size<<2];

void build(int p,int l,int r)
{
    tree[p].l = l,tree[p].r = r,tree[p].maxx = -INF;
    if(l == r)  return ;
    int mid = (l + r) >> 1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
}

void change(int p,int id,int w)
{
    if(tree[p].l == tree[p].r)
    {
        tree[p].maxx = w;
        return ;
    }
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(id <= mid)   change(p<<1,id,w);
    else            change(p<<1|1,id,w);
    tree[p].maxx = max(tree[p<<1].maxx,tree[p<<1|1].maxx);
}

int query(int p,int l,int r)
{
    if(l <= tree[p].l && tree[p].r <= r)    return tree[p].maxx;
    int mid = (tree[p].l + tree[p].r) >> 1;
    int ans = -INF;
    if(l <= mid)    ans = max(ans,query(p<<1,l,r));
    if(mid < r)     ans = max(ans,query(p<<1|1,l,r));
    return ans;
}

int ask(int x,int y)
{
    int ans = -INF;
    while(top[x] != top[y])
    {
        if(deep[top[x]] < deep[top[y]]) swap(x,y);
        ans = max(ans,query(1,w[top[x]],w[x]));
        x = fa[top[x]];
    }
    if(deep[x] > deep[y])   swap(x,y);
    if(x != y)
    {
        ans = max(ans,query(1,w[x]+1,w[y]));
    }
    return ans;
}
//END
int ed[size][3];
void init()
{
    memset(edges,0,sizeof(edges));
    memset(head,0,sizeof(head));
    memset(tree,0,sizeof(tree));
    tot = 0;
    dfs_clock = 0;
}
int n;
char dora[450];
int main()
{
    int t;
    scanf("%d",&t);
    while(t --)
    {
        init();
        scanf("%d",&n);
        for(int i = 1;i < n;i ++)
        {
            scanf("%d%d%d",&ed[i][0],&ed[i][1],&ed[i][2]);
            build(ed[i][0],ed[i][1]);
            build(ed[i][1],ed[i][0]);
        }
        dfs(1,1,1);
        dfs2(1,1);
        build(1,1,n);
        for(int i = 1;i < n;i ++)
        {
            if(deep[ed[i][0]] > deep[ed[i][1]]) swap(ed[i][0],ed[i][1]);
            change(1,w[ed[i][1]],ed[i][2]);
        }
        while(233)
        {
            scanf("%s",dora);
            if(dora[0] == 'D')  break;
            if(dora[0] == 'Q')
            {
                int a,b;
                scanf("%d%d",&a,&b);
                printf("%d\n",ask(a,b));
            }
            else
            {
                int a,b;
                scanf("%d%d",&a,&b);
                change(1,w[ed[a][1]],b);
            }
        }
        puts("");
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: