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

45 篇文章 1 订阅
17 篇文章 0 订阅
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

分析:
好好的读过题后,题目可以总结为:一个树形结构,对于树上的路径查询或修改
可以想到用树链剖分完成

对于每一个结点的两部分,我们黑白染色
剖完之后,线段树的每个结点中代表的是树上的一条链
那么我们在这条链上维护八个值:
dis[1][1]:左端黑色<—>右端黑色
dis[1][0]:左端黑色<—>右端白色
dis[0][1]:左端白色<—>右端黑色
dis[0][0]:左端白色<—>右端白色
rs[1]:右端黑色向左能够延伸的最长距离
rs[0]:右端白色向左能够延伸的最长距离
ls[1]:左端黑色向右能够延伸的最长距离
ls[0]:左端白色向右能够延伸的最长距离

随意维护上述信息

我们在询问一条链的时候,分成了两部分:x—>lca,lca—>y
在用树链剖分进行询问的时候,分别维护两条链
最后在把两条链合并,但是在合并的时候,需要把一条链翻转
才能保证答案正确

翻转链:
swap(dis[0][1],dis[1][0]) s w a p ( d i s [ 0 ] [ 1 ] , d i s [ 1 ] [ 0 ] )
swap(ls[0],rs[0]) s w a p ( l s [ 0 ] , r s [ 0 ] )
swap(ls[1],rs[1]) s w a p ( l s [ 1 ] , r s [ 1 ] )

tip

写着写着就想到了这道题
不好写。。。orz

ask的时候不要乱swap
使用新的线段树结点时不要忘了初始化

查了半天错,发现是update写残了。。。
不知道为什么最后一步的update怎么也传不出正确答案了。。。怎么改不知道啊

于是就把update由函数变成重载运算符
但是这样还是不行,于是又重载了减号(翻转)
这样才过了样例,但是WA了
只能看前辈的代码了。。。

不知道为什么今天写什么都A不掉。。。—>AC代码

//A不了C代码
#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;

const int INF=1<<30;
const int N=30010;
int n,m,st[N],tot=0,mp[N][2];
struct node{
    int y,nxt;
};
node way[N<<1];
struct Tree{
    int dis[2][2],rs[2],ls[2];
    Tree()
    {
        memset(dis,0,sizeof(dis));
        memset(rs,0,sizeof(rs));
        memset(ls,0,sizeof(ls));
        dis[0][1]=dis[1][0]=-INF;
    }

    Tree(int x)
    {
        if (mp[x][0]&&mp[x][1]) {
            ls[0]=ls[1]=rs[0]=rs[1]=dis[0][1]=dis[1][0]=2;
            dis[0][0]=dis[1][1]=1;
        } else if (mp[x][0]) {
            ls[0]=rs[0]=dis[0][0]=1;
            ls[1]=rs[1]=0;
            dis[0][1]=dis[1][0]=dis[1][1]=-INF;
        } else if (mp[x][1]) {
            ls[1]=rs[1]=dis[1][1]=1;
            ls[0]=rs[0]=0;
            dis[0][0]=dis[0][1]=dis[1][0]=-INF;
        } else {
            ls[0]=ls[1]=rs[0]=rs[1]=0;
            dis[0][1]=dis[1][0]=dis[1][1]=dis[0][0]=-INF;
       }
    }

    friend Tree operator +(const Tree &l,const Tree &r)
    {
        Tree ans;

        ans.ls[0]=max(l.ls[0] , max(l.dis[0][1]+r.ls[1] , l.dis[0][0]+r.ls[0]));
        ans.ls[1]=max(l.ls[1] , max(l.dis[1][1]+r.ls[1] , l.dis[1][0]+r.ls[0]));
        ans.rs[0]=max(r.rs[0] , max(r.dis[1][0]+l.rs[1] , r.dis[0][0]+l.rs[0]));
        ans.rs[1]=max(r.rs[1] , max(r.dis[0][1]+l.rs[0] , r.dis[1][1]+l.rs[1]));

        ans.dis[0][0]=max(-INF , max(l.dis[0][0]+r.dis[0][0] , l.dis[0][1]+r.dis[1][0]));
        ans.dis[0][1]=max(-INF , max(l.dis[0][0]+r.dis[0][1] , l.dis[0][1]+r.dis[1][1]));
        ans.dis[1][0]=max(-INF , max(l.dis[1][0]+r.dis[0][0] , l.dis[1][1]+r.dis[1][0]));
        ans.dis[1][1]=max(-INF , max(l.dis[1][0]+r.dis[0][1] , l.dis[1][1]+r.dis[1][1]));

        return ans;
    }   

    Tree operator -() const
    {
        Tree ans;
        ans.ls[0]=rs[0] , ans.ls[1]=rs[1];
        ans.rs[0]=ls[0] , ans.rs[1]=ls[1];
        ans.dis[0][0]=dis[0][0] , ans.dis[0][1]=dis[1][0];
        ans.dis[1][0]=dis[0][1] , ans.dis[1][1]=dis[1][1];
        return ans;
    }
};
Tree t[N<<2];
int num[N],shu[N],size[N],son[N],top[N],deep[N],pre[N],clo=0;

void add(int x,int y)
{
    tot++;
    way[tot].y=y; way[tot].nxt=st[x]; st[x]=tot;
    tot++;
    way[tot].y=x; way[tot].nxt=st[y]; st[y]=tot;
}

void dfs_1(int now,int fa,int dep)
{
    deep[now]=dep;
    pre[now]=fa;
    size[now]=1;
    int maxx=0;
    for (int i=st[now];i;i=way[i].nxt)
        if (way[i].y!=fa)
        {
            dfs_1(way[i].y,now,dep+1);
            size[now]+=size[way[i].y];
            if (size[way[i].y]>maxx)
                maxx=size[way[i].y],son[now]=way[i].y;
        }
}

void dfs_2(int now,int fa)
{
    if (son[fa]!=now) top[now]=now;
    else top[now]=top[fa];
    num[now]=++clo; shu[num[now]]=now;
    if (son[now])
    {
        dfs_2(son[now],now);
        for (int i=st[now];i;i=way[i].nxt)
            if (way[i].y!=fa&&way[i].y!=son[now])
                dfs_2(way[i].y,now);
    }
}

void build(int bh,int l,int r)
{
    if (l==r)
    {
        t[bh]=Tree(shu[l]);
        return;
    }
    int mid=(l+r)>>1;
    build(bh<<1,l,mid);
    build(bh<<1|1,mid+1,r);
    t[bh]=t[bh<<1]+t[bh<<1|1];
}

void change(int bh,int l,int r,int x)
{
    if (l==r)
    {
        t[bh]=Tree(shu[l]);
        return;
    }
    int mid=(l+r)>>1;
    if (x<=mid) change(bh<<1,l,mid,x);
    else change(bh<<1|1,mid+1,r,x);
    t[bh]=t[bh<<1]+t[bh<<1|1];
}

Tree asksum(int bh,int l,int r,int L,int R)
{
    if (l>=L&&r<=R) return t[bh];
    int mid=(l+r)>>1;
    if (R<=mid) return asksum(bh<<1,l,mid,L,R);
    else if (L>mid) return asksum(bh<<1|1,mid+1,r,L,R);
    return asksum(bh<<1,l,mid,L,R)+asksum(bh<<1|1,mid+1,r,L,R);
}

int ask(int x,int y)
{
    Tree A,B,C;
    int f1=top[x];
    int f2=top[y];
    while (f1!=f2)
    {
        if (deep[f1]>deep[f2]) {
            A=asksum(1,1,n,num[f1],num[x])+A;
            x=pre[f1];
            f1=top[x];
        }  
        else {
            B=asksum(1,1,n,num[f2],num[y])+B;
            y=pre[f2];
            f2=top[y];
        }
    }
    if (deep[x]>deep[y]) 
        A=asksum(1,1,n,num[y],num[x])+A;
    else B=asksum(1,1,n,num[x],num[y])+B;

    A=-A+B;

    return max(A.ls[0],A.ls[1]);
}

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);
    }
    char s[5];
    for (int i=1;i<=n;i++)
    {
        scanf("%s",s);
        for (int j=0;j<=1;j++)
            if (s[j]=='.') mp[i][j]=1;
            else mp[i][j]=0;
    }

    dfs_1(1,0,1);
    dfs_2(1,0);
    build(1,1,n);

    int x,y;
    for (int i=1;i<=m;i++)
    {
        scanf("%s",s);
        if (s[0]=='C')
        {
            scanf("%d",&x); scanf("%s",s);
            for (int j=0;j<=1;j++)
                if (s[j]=='.') mp[x][j]=1;
                else mp[x][j]=0;
            change(1,1,n,num[x]);     //num[x]
        }
        else
        {
            scanf("%d%d",&x,&y);
            printf("%d\n",ask(x,y));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值