bzoj 2325: [ZJOI2011]道馆之战 (树链剖分+线段树)

2325: [ZJOI2011]道馆之战

Time Limit: 40 Sec   Memory Limit: 256 MB
Submit: 1175   Solved: 429
[ Submit][ Status][ Discuss]

Description

口袋妖怪(又名神奇宝贝或宠物小精灵)红/蓝/绿宝石中的水系道馆需要经过三个冰地才能到达馆主的面前,冰地中
的每一个冰块都只能经过一次。当一个冰地上的所有冰块都被经过之后,到下一个冰地的楼梯才会被打开。三个冰
地分别如下:
当走出第三个冰地之后,就可以与馆主进行道馆战了。馆主发现这个难度太小,导致经常有挑战者能通过,为了加
大难度,将道馆分成了n个房间,每个房间中是两个冰块或障碍,表示一列冰地。任意两个房间之间均有且仅有一
条路径相连,即这n个房间构成一个树状结构。每个房间分成了A和B两个区域,每一区域都是一个薄冰块或者障碍
物。每次只能移动到相邻房间的同一类区域(即若你现在在这个房间的A区域,那么你只能移动到相邻房间的A区域)
或这个房间的另一区域。现在挑战者从房间u出发,馆主在房间v,那么挑战者只能朝接近馆主所在房间的方向过去
。一开始挑战者可以在房间u的任意一个冰块区域内。如果挑战者踩过的冰块数达到了最大值(即没有一种方案踩过
的冰块数更多了),那么当挑战者走到最后一个冰块上时,他会被瞬间传送到馆主面前与馆主进行道馆战。自从馆
主修改规则后已经经过了m天,每天要么是有一个挑战者来进行挑战,要么就是馆主将某个房间进行了修改。对于
每个来的挑战者,你需要计算出他若要和馆主进行战斗需要经过的冰块数。

Input

第一行包含两个正整数n和m。第2行到第n行,每行包含两个正整数x和y,表示一条连接房间x和房间y的边。房间编
号为1…n。接下来n行,每行包含两个字符。第n + k行表示房间k的两个区域,第一个字符为A区域,第二个字符为
B区域。其中“.”(ASCII码为46)表示是薄冰块,“#”(ASCII码为35)表示是障碍物。最后的m行,每行一个操作:
l C u s:将房间u里的两个区域修改为s。
l Q u v:询问挑战者在房间u,馆主在房间v时,挑战者能与馆主进行挑战需要踩的冰块数。如果房间u的两个区域
都是障碍物,那么输出0。
N≤ 30 000
M ≤ 80 000

Output

包含若干行,每行一个整数。即对于输入中的每个询问,依次输出一个答案。

Sample Input

5 3
1 2
2 3
2 4
1 5
.#
..
#.
.#
..
Q 5 3
C 1 ##
Q 4 5

Sample Output

6
3

HINT

Source

[ Submit][ Status][ Discuss]

题解:树链剖分+线段树

线段树维护从左上到右上,从左上到右下,从左下到右上,从左下到右下走过冰块的最大值

以及从一段区间的左上,左下,右上,右下出发分别能达到能到达的最远距离。

剩下的就是树链剖分的问题啦。

注意从x->lca(x,y)得到的信息,需要进行适当的调整,再与lca(x,y)->y合并。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 30003
#define inf 1000000000
using namespace std;
struct data{
    int l1,l2,r1,r2;
    int d1,d2,d3,d4;
}tr[N*4];
char s[N][3];
int n,m,point[N*2],nxt[N*2],v[N*2],tot,sz;
int belong[N],pos[N],size[N],q[N],son[N],deep[N],fa[N];
void add(int x,int y)
{
    tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; 
    tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void dfs(int x,int f)
{
    size[x]=1;
    deep[x]=deep[f]+1;
    for (int i=point[x];i;i=nxt[i]){
        if (v[i]==f) continue;
        fa[v[i]]=x;
        dfs(v[i],x);
        size[x]+=size[v[i]];
        if (size[v[i]]>size[son[x]])
         son[x]=v[i];
    }
}
void dfs1(int x,int chain)
{
    pos[x]=++sz; q[sz]=x; belong[x]=chain;
    if (!son[x]) return;
    dfs1(son[x],chain);
    for (int i=point[x];i;i=nxt[i])
     if(v[i]!=son[x]&&v[i]!=fa[x])
      dfs1(v[i],v[i]);
}
data update(data x,data y)
{
    data a; 
    a.d1=max(x.d1+y.d1,x.d2+y.d3); if (a.d1<0) a.d1=-inf;
    a.d2=max(x.d2+y.d4,x.d1+y.d2); if (a.d2<0) a.d2=-inf;
    a.d3=max(x.d3+y.d1,x.d4+y.d3); if (a.d3<0) a.d3=-inf;
    a.d4=max(x.d3+y.d2,x.d4+y.d4); if (a.d4<0) a.d4=-inf;
    a.l1=x.l1; a.l2=x.l2; a.r1=y.r1; a.r2=y.r2;
    a.l1=max(a.l1,max(x.d1+y.l1,x.d2+y.l2)); if (a.l1<0) a.l1=-inf;
    a.l2=max(a.l2,max(x.d4+y.l2,x.d3+y.l1)); if (a.l2<0) a.l2=-inf;
    a.r1=max(a.r1,max(y.d1+x.r1,y.d3+x.r2)); if (a.r1<0) a.r1=-inf;
    a.r2=max(a.r2,max(y.d2+x.r1,y.d4+x.r2)); if (a.r2<0) a.r2=-inf; 
    return a;
}
void build(int now,int l,int r)
{
    if (l==r) {
        int x=q[l];
        tr[now].d1=tr[now].d2=tr[now].d3=tr[now].d4=-inf;
        tr[now].l1=tr[now].l2=tr[now].r1=tr[now].r2=-inf;
        if (s[x][0]=='.') tr[now].d1=tr[now].l1=tr[now].r1=1;
        if (s[x][1]=='.') tr[now].d4=tr[now].l2=tr[now].r2=1;
        if (s[x][1]=='.'&&s[x][0]=='.')
         tr[now].d2=tr[now].d3=tr[now].l1=tr[now].l2=tr[now].r1=tr[now].r2=2;
        //cout<<tr[now].l1<<" "<<tr[now].l2<<" "<<tr[now].r1<<" "<<tr[now].r2<<endl;
        return;
    }
    int mid=(l+r)/2;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    tr[now]=update(tr[now<<1],tr[now<<1|1]);
    //cout<<tr[now].l1<<" "<<tr[now].l2<<" "<<tr[now].r1<<" "<<tr[now].r2<<endl;
}
void pointchange(int now,int l,int r,int pos)
{
    if(l==r) {
        int x=q[l];
        tr[now].d1=tr[now].d2=tr[now].d3=tr[now].d4=-inf;
        tr[now].l1=tr[now].l2=tr[now].r1=tr[now].r2=-inf;
        if (s[x][0]=='.') tr[now].d1=tr[now].l1=tr[now].r1=1;
        if (s[x][1]=='.') tr[now].d4=tr[now].l2=tr[now].r2=1;
        if (s[x][1]=='.'&&s[x][0]=='.')
         tr[now].d2=tr[now].d3=tr[now].l1=tr[now].l2=tr[now].r1=tr[now].r2=2;
        return;
    }
    int mid=(l+r)/2;
    if (pos<=mid) pointchange(now<<1,l,mid,pos);
    else pointchange(now<<1|1,mid+1,r,pos);
    tr[now]=update(tr[now<<1],tr[now<<1|1]);
    //cout<<tr[now].l1<<" "<<tr[now].l2<<" "<<tr[now].r1<<" "<<tr[now].r2<<endl;
}
data query(int now,int l,int r,int ll,int rr)
{
    if (ll<=l&&r<=rr) return tr[now];
    int mid=(l+r)/2;
    if (ll<=mid&&rr>mid) return update(query(now<<1,l,mid,ll,rr),query(now<<1|1,mid+1,r,ll,rr));
    else if (ll<=mid) return query(now<<1,l,mid,ll,rr);
    else return query(now<<1|1,mid+1,r,ll,rr);
}
void clear(data &a)
{
    a.d1=a.d2=a.d3=a.d4=0;
    a.l1=a.l2=a.r1=a.r2=0;
}
int solve(int x,int y)
{
    if (s[x][0]=='#'&&s[x][1]=='#') return 0;
    data a,b; 
    clear(a); clear(b);
    while (belong[x]!=belong[y]) {
        if (deep[belong[x]]>deep[belong[y]]) {
            a=update(query(1,1,n,pos[belong[x]],pos[x]),a);
            x=fa[belong[x]];
        }
        else {
            b=update(query(1,1,n,pos[belong[y]],pos[y]),b);
            y=fa[belong[y]];
        }
    }
    if (deep[x]<deep[y]) b=update(query(1,1,n,pos[x],pos[y]),b);
    else a=update(query(1,1,n,pos[y],pos[x]),a);
    swap(a.l1,a.r1); swap(a.l2,a.r2); swap(a.d2,a.d3);
    a=update(a,b);
    return max(a.l1,a.l2);
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<n;i++) {
        int x,y; scanf("%d%d",&x,&y);
        add(x,y);
    }
    dfs(1,0); dfs1(1,1);
    for (int i=1;i<=n;i++) scanf("%s",s[i]);
    build(1,1,n);
    //for (int i=1;i<=n;i++) cout<<pos[i]<<" "; cout<<endl;
    //for (int i=1;i<=n;i++) cout<<q[i]<<" "; cout<<endl;
    //cout<<tr[1].l1<<" "<<tr[1].l2<<" "<<tr[1].r1<<" "<<tr[1].r2<<endl;
    for (int i=1;i<=m;i++) {
        char opt[10];
        int x,y; 
        scanf("%s",opt);
        if (opt[0]=='Q') {
            scanf("%d%d",&x,&y);
            printf("%d\n",solve(x,y));
        }
        else {
            scanf("%d",&x);
            scanf("%s",s[x]);
            pointchange(1,1,n,pos[x]);
        }
    }
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值