【POJ3237】Tree(树链剖分)

23 篇文章 0 订阅
70 篇文章 0 订阅

题目描述

传送门

题解

想了一节语文课。
树链剖分模板题。但是这个是要修改边权,比修改点权麻烦一点。
和修改点权一样,重儿子、父亲等东西都需要记录,不同的是需要再记录一下每个点到它重儿子的边的编号,以及每个点到它的父亲的边的编号。
这样的话,我们在查询和修改的时候,大部分和修改点权一样,不同的是:如果u和v不在一条链上,f1为u所在的链上最顶端的点,f2为v所在的链上最顶端的点(假设f1深度大于f2),如果我们修改点权的话只需要修改f1到u的即可,但是要修改边权的话需要修改f1的父亲边到u的父亲边,然后再将u跳到f1的父亲。如果到了最后u和v到了一条链上,那么我们需要把u的儿子边到v的父亲边修改(假设
v的深度大于u),而如果u=v的话应该直接退出。
上述的预处理都通过两个dfs处理出来。
线段树维护最大值的话很简单,只需要再维护一个最小值。如果到了取反的话就把最大值和最小值分别*(-1)再交换。
自己体会一下,实在不行画一棵树搞一搞,没思路的时候上语文课的时候想一想也许会有意想不到的效果。原则就是修改和查询的时候不重不漏不多。

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

const int max_n=1e4+5;
const int max_e=max_n*2;
const int mininf=-2100000000;
const int max_N=max_n*4;

int T,n,x,y,z,N,u,t,ans;
int tot,next[max_e],point[max_n],v[max_e],c[max_e];
int size[max_n],h[max_n],father[max_n],son[max_n],top[max_n];
int sonedge[max_n],faedge[max_n],num[max_e],val[max_e];
int maxn[max_N],minn[max_N],delta[max_N];
char s[10];

inline void clear(){
    memset(next,0,sizeof(next));
    memset(point,0,sizeof(point));
    memset(v,0,sizeof(v));
    memset(c,0,sizeof(c));
    memset(size,0,sizeof(size));
    memset(h,0,sizeof(h));
    memset(father,0,sizeof(father));
    memset(son,0,sizeof(son));
    memset(sonedge,0,sizeof(sonedge));
    memset(faedge,0,sizeof(faedge));
    memset(top,0,sizeof(top));
    memset(num,0,sizeof(num));
    memset(val,0,sizeof(val));
    memset(maxn,0,sizeof(maxn));
    memset(minn,0,sizeof(minn));
    memset(delta,0,sizeof(delta));
    tot=0;
    n=x=y=z=N=u=t=ans=0;
}

inline void add(int x,int y,int z){
    ++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
    ++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}

inline void dfs_1(int x,int fa,int dep){
    size[x]=1; h[x]=dep; father[x]=fa;
    int maxson=0;
    for (int i=point[x];i;i=next[i])
      if (v[i]!=fa){
        faedge[v[i]]=i;
        dfs_1(v[i],x,dep+1);
        size[x]+=size[v[i]];
        if (maxson<size[v[i]]){
            maxson=size[v[i]];
            son[x]=v[i];
            sonedge[x]=i;
          }
      }
}

inline void dfs_2(int x,int fa){
    if (son[fa]!=x) top[x]=x;
    else top[x]=top[fa];

    for (int i=point[x];i;i=next[i])
      if (v[i]==son[x]){
        num[i]=++N;
        val[N]=c[i];
        dfs_2(son[x],x);
      }

    for (int i=point[x];i;i=next[i])
      if (v[i]!=fa&&v[i]!=son[x]){
        num[i]=++N;
        val[N]=c[i];
        dfs_2(v[i],x);
      }
}

inline void update(int now){
    maxn[now]=max(maxn[now<<1],maxn[now<<1|1]);
    minn[now]=min(minn[now<<1],minn[now<<1|1]);
}

inline void pushdown(int now,int l,int r,int mid){
    if (delta[now]%2){
        swap(maxn[now<<1],minn[now<<1]);
        maxn[now<<1]*=-1;
        minn[now<<1]*=-1;
        delta[now<<1]++;
        swap(maxn[now<<1|1],minn[now<<1|1]);
        maxn[now<<1|1]*=-1;
        minn[now<<1|1]*=-1;
        delta[now<<1|1]++;
    }
    delta[now]=0;
}

inline void build(int now,int l,int r){
    int mid=(l+r)>>1;
    if (l==r){
        maxn[now]=val[l];
        minn[now]=val[l];
        return;
    }
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    update(now);
}

inline void point_change(int now,int l,int r,int x,int v){
    int mid=(l+r)>>1;
    if (l==r){
        maxn[now]=v;
        minn[now]=v;
        return;
    }
    pushdown(now,l,r,mid);
    if (x<=mid)
      point_change(now<<1,l,mid,x,v);
    else
      point_change(now<<1|1,mid+1,r,x,v);
    update(now);
}

inline void interval_change(int now,int l,int r,int lrange,int rrange){
    int mid=(l+r)>>1;
    if (lrange<=l&&r<=rrange){
        swap(maxn[now],minn[now]);
        maxn[now]*=-1;
        minn[now]*=-1;
        delta[now]++;
        return;
    }
    pushdown(now,l,r,mid);
    if (lrange<=mid)
      interval_change(now<<1,l,mid,lrange,rrange);
    if (mid+1<=rrange)
      interval_change(now<<1|1,mid+1,r,lrange,rrange);
    update(now);
}

inline int query(int now,int l,int r,int lrange,int rrange){
    int mid=(l+r)>>1,ans=mininf;
    if (lrange<=l&&r<=rrange) return maxn[now];
    pushdown(now,l,r,mid);
    if (lrange<=mid)
      ans=max(ans,query(now<<1,l,mid,lrange,rrange));
    if (mid+1<=rrange)
      ans=max(ans,query(now<<1|1,mid+1,r,lrange,rrange));
    return ans;
}

inline void NEGATE(int u,int t){
    int f1=top[u],f2=top[t];
    while (f1!=f2){
        if (h[f1]<h[f2]){
            swap(u,t);
            swap(f1,f2);
        }
        interval_change(1,1,N,num[faedge[f1]],num[faedge[u]]);
        u=father[f1];
        f1=top[u];
    }
    if (u==t) return;
    if (h[u]>h[t]) swap(u,t);
    interval_change(1,1,N,num[sonedge[u]],num[faedge[t]]);
}

inline int QUERY(int u,int t){
    int ans=mininf;
    int f1=top[u],f2=top[t];
    while (f1!=f2){
        if (h[f1]<h[f2]){
            swap(u,t);
            swap(f1,f2);
        }
        ans=max(ans,query(1,1,N,num[faedge[f1]],num[faedge[u]]));
        u=father[f1];
        f1=top[u];
    }
    if (u==t) return ans;
    if (h[u]>h[t]) swap(u,t);
    ans=max(ans,query(1,1,N,num[sonedge[u]],num[faedge[t]]));
    return ans;
}

int main(){
    scanf("%d",&T);
    while (T--){
        clear();

        scanf("%d",&n);
        for (int i=1;i<n;++i){
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z);
        }

        dfs_1(1,0,1);

        dfs_2(1,0);

        build(1,1,N);
        scanf("%s",s);
        while (strlen(s)>4){
            scanf("%d%d",&u,&t);
            switch(s[0]){
                case 'C':{
                    if (num[u*2-1]) x=num[u*2-1];
                    else x=num[u*2];
                    point_change(1,1,N,x,t);
                    break;
                }
                case 'N':{
                    NEGATE(u,t);
                    break;
                }
                case 'Q':{
                    ans=QUERY(u,t);
                    printf("%d\n",ans);
                    break;
                }
            }
            scanf("%s",s);
        }
    }
}

总结

①线段树只要牵扯到区间修改,所有的操作之前都要pushdown,所有的修改操作之后都要update

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
POJ1753题目为"Flip Game",题目给出了一个4x4的棋盘,每个格子有黑色或白色,每次翻转一个格子会同时翻转它上下左右四个格子的颜色,目标是把整个棋盘都变为同一种颜色,求把棋盘变成同种颜色的最小步数。 解题思路: 一般关于棋盘变色的题目,可以考虑使用搜索来解决。对于POJ1753题目,可以使用广度优先搜索(BFS)来解决。 首先,对于每个格子,定义一个状态,0表示当前格子是白色,1表示当前格子是黑色。 然后,我们可以把棋盘抽象成一个长度为16的二进制数,将所有格子的状态按照从左往右,从上往下的顺序排列,就可以用一个16位的二进制数表示整个棋盘的状态。例如,一个棋盘状态为: 0101 1010 0101 1010 则按照从左往右,从上往下的顺序把所有格子的状态连接起来,即可得到该棋盘的状态为"0101101001011010"。 接着,我们可以使用队列来实现广度优先搜索。首先将初始状态加入队列中,然后对于队列中的每一个状态,我们都尝试将棋盘上的每个格子翻转一次,生成一个新状态,将新状态加入队列中。对于每一个新状态,我们也需要记录它是从哪个状态翻转得到的,以便在得到最终状态时能够输出路径。 在搜索过程中,我们需要维护每个状态离初始状态的步数,即将该状态转换为最终状态需要的最小步数。如果我们找到了最终状态,就可以输出答案,即最小步数。 代码实现:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Clove_unique

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值