【BZOJ2325】[ZJOI2011]道馆之战

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

题解:显然的线段树+树剖,但是线段树要维护些什么呢?f[0/1][0/1]表示从左边的A/B区域走到右边的A/B区域最多能踩多少冰块,lm[0/1]代表从左边的A/B区域往右走最多能踩多少冰块,rm[0/1]代表从右边的A/B区域往左走最多能踩多少冰块。在区间合并的时候维护这3个东西。并且不难发现,这3个东西是支持翻转操作的,所以查询的时候就容易多了。

#include <cstdio>
#include <cstring>
#include <iostream>
#define lson x<<1
#define rson x<<1|1
using namespace std;
const int maxn=30010;
const int inf=0x1f1f1f1f;
int max(int a,int b,int c) {return max(max(a,b),c);}
int n,m,cnt,ans;
int siz[maxn],dep[maxn],fa[maxn],son[maxn],top[maxn],to[maxn<<1],next[maxn<<1],head[maxn],st[maxn],p[maxn],q[maxn];
int v[maxn][2];
char str[10];
struct node
{
int a[2][2],lm[2],rm[2];
node () {memset(a,0,sizeof(a)),memset(lm,0,sizeof(lm)),memset(rm,0,sizeof(rm));}
node (int _)
{
lm[0]=rm[0]=max(v[_][0],v[_][0]+v[_][1],0),a[0][0]=v[_][0];
lm[1]=rm[1]=max(v[_][1],v[_][0]+v[_][1],0),a[1][1]=v[_][1];
a[0][1]=a[1][0]=v[_][0]+v[_][1];
}
int * operator [] (int b){return a[b];}
void rev()
{
swap(a[0][1],a[1][0]),swap(lm[0],rm[0]),swap(lm[1],rm[1]);
}
node operator + (node b)
{
node c;
c[0][0]=max(a[0][0]+b[0][0],a[0][1]+b[1][0],-inf);
c[0][1]=max(a[0][0]+b[0][1],a[0][1]+b[1][1],-inf);
c[1][0]=max(a[1][0]+b[0][0],a[1][1]+b[1][0],-inf);
c[1][1]=max(a[1][0]+b[0][1],a[1][1]+b[1][1],-inf);
c.lm[0]=max(lm[0],a[0][0]+b.lm[0],a[0][1]+b.lm[1]);
c.lm[1]=max(lm[1],a[1][0]+b.lm[0],a[1][1]+b.lm[1]);
c.rm[0]=max(b.rm[0],rm[0]+b[0][0],rm[1]+b[1][0]);
c.rm[1]=max(b.rm[1],rm[0]+b[0][1],rm[1]+b[1][1]);
return c;
}
}s[maxn<<2];
inline void add(int a,int b)
{
to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
void dfs1(int x)
{
siz[x]=1;
for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[x])
{
fa[to[i]]=x,dep[to[i]]=dep[x]+1,dfs1(to[i]),siz[x]+=siz[to[i]];
if(siz[to[i]]>siz[son[x]]) son[x]=to[i];
}
}
void dfs2(int x,int tp)
{
top[x]=tp,p[x]=++p[0],q[p[0]]=x;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[x]&&to[i]!=son[x]) dfs2(to[i],to[i]);
}
void build(int l,int r,int x)
{
if(l==r)
{
s[x]=node(q[l]);
return ;
}
int mid=(l+r)>>1;
build(l,mid,lson),build(mid+1,r,rson);
s[x]=s[lson]+s[rson];
}
void updata(int l,int r,int x,int a)
{
if(l==r)
{
s[x]=node(q[l]);
return ;
}
int mid=(l+r)>>1;
if(a<=mid) updata(l,mid,lson,a);
else updata(mid+1,r,rson,a);
s[x]=s[lson]+s[rson];
}
node query(int l,int r,int x,int a,int b)
{
if(a<=l&&r<=b) return s[x];
int mid=(l+r)>>1;
if(b<=mid) return query(l,mid,lson,a,b);
if(a>mid) return query(mid+1,r,rson,a,b);
return query(l,mid,lson,a,b)+query(mid+1,r,rson,a,b);
}
void ask(int x,int y)
{
node sx,sy,t;
while(top[x]!=top[y])
{
if(dep[top[x]]>dep[top[y]]) t=query(1,n,1,p[top[x]],p[x]),t.rev(),sx=sx+t,x=fa[top[x]];
else sy=query(1,n,1,p[top[y]],p[y])+sy,y=fa[top[y]];
}
if(dep[x]>dep[y]) t=query(1,n,1,p[y],p[x]),t.rev(),sx=sx+t;
else sy=query(1,n,1,p[x],p[y])+sy;
sx=sx+sy;
printf("%d\n",max(sx.lm[0],sx.lm[1]));
}
inline int rd()
{
int ret=0,f=1; char gc=getchar();
while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();}
while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar();
return ret*f;
}
int main()
{
n=rd(),m=rd();
int i,a,b;
memset(head,-1,sizeof(head));
for(i=1;i<n;i++) a=rd(),b=rd(),add(a,b),add(b,a);
dep[1]=1,dfs1(1),dfs2(1,1);
for(i=1;i<=n;i++) scanf("%s",str),v[i][0]=(str[0]=='.')?1:-inf,v[i][1]=(str[1]=='.')?1:-inf;
build(1,n,1);
for(i=1;i<=m;i++)
{
scanf("%s",str);
if(str[0]=='Q') a=rd(),b=rd(),ask(a,b);
else a=rd(),scanf("%s",str),v[a][0]=(str[0]=='.')?1:-inf,v[a][1]=(str[1]=='.')?1:-inf,updata(1,n,1,p[a]);
}
return 0;
}//5 3 1 2 2 3 2 4 1 5 .# .. #. .# .. Q 5 3 C 1 ## Q 4 5

【BZOJ2325】[ZJOI2011]道馆之战 线段树+树链剖分的更多相关文章

  1. BZOJ2325[ZJOI2011]道馆之战——树链剖分+线段树

    题目描述 口袋妖怪(又名神奇宝贝或宠物小精灵)红/蓝/绿宝石中的水系道馆需要经过三个冰地才能到达馆主的面前,冰地中 的每一个冰块都只能经过一次.当一个冰地上的所有冰块都被经过之后,到下一个冰地的楼梯才 ...

  2. bzoj2325 [ZJOI2011]道馆之战

    Description 口袋妖怪(又名神奇宝贝或宠物小精灵)红/蓝/绿宝石中的水系道馆需要经过三个冰地才能到达馆主的面前,冰地中的每一个冰块都只能经过一次.当一个冰地上的所有冰块都被经过之后,到下一个 ...

  3. BZOJ2325 [ZJOI2011]道馆之战 树链剖分 线段树

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ2325 题意概括 给你一棵N个点的树,树上的每个节点有A,B两块区域,且每种区域有两种状态:可以走的 ...

  4. bzoj2325 [ZJOI2011]道馆之战 树链剖分+DP+类线段树最大字段和

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=2325 题解 可以参考线段树动态维护最大子段和的做法. 对于线段树上每个节点 \(o\),维护 ...

  5. bzoj千题计划243:bzoj2325: [ZJOI2011]道馆之战

    http://www.lydsy.com/JudgeOnline/problem.php?id=2325 设线段树节点区间为[l,r] 每个节点维护sum[0/1][0/1]  从l的A/B区域到r的 ...

  6. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1153  Solved: 421[Submit][Statu ...

  7. 【bzoj2325】[ZJOI2011]道馆之战 树链剖分+线段树区间合并

    题目描述 给定一棵树,每个节点有上下两个格子,每个格子的状态为能走或不能走.m次操作,每次修改一个节点的状态,或询问:把一条路径上的所有格子拼起来形成一个宽度为2的长方形,从起点端两个格子的任意一个开 ...

  8. 【题解】Luogu P4679 [ZJOI2011]道馆之战

    原题传送门 码农题树剖好题,口袋妖怪是个好玩的游戏 这道题要用树链剖分,我博客里有对树链剖分的详细介绍 下文左右就代表树的节点按dfs序后的左右,上.下分别表示每个节点的A.B区域 考虑在链上的情况, ...

  9. 线段树&数链剖分

    傻逼线段树,傻逼数剖 线段树 定义: 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 使用线段树可以快速的查找某一个节点在若干条线段中出现 ...

随机推荐

  1. VLOOKUP多条件查找不使用辅助列

    问题描述: 可能很多人在使用VLOOKUP的时候,对于多条件时,则会使用辅助列进行查找,那么不使用辅助列是否也可以呢? 答案是:必须可以. 问题处理: 1.使用辅助列 2.不使用辅助列的操作如下: 公 ...

  2. android js 互相调用

    代码地址如下:http://www.demodashi.com/demo/13107.html android js 互相调用 第二版 支持js匿名函数接收 支持js json对象接收 支持js函数返 ...

  3. 阿里云创建RAM子账号

    操作步骤 创建子账号 主账号导航至 访问控制 > 用户管理 页面.如下图所示: 单击右上角的 新建用户,如下图所示: 填写弹出框的各配置项,如下图所示: 单击 确定,即可创建子账号. 允许子账号 ...

  4. Centos下Subversion 服务器安装配置

    1.安装 # yum install subversion 2. svn配置 建立svn版本库目录可建多个:2.1 新建文件夹: # mkdir -p /opt/svndata/repos 2.2 建 ...

  5. Linux中如何配置IP

      与网络相关的文件:1) /etc/sysconfig/network   设置主机名称及能否启动Network2) /etc/sysconfig/network-scripts/ifcfg-eth ...

  6. opengl剪裁空间和视口空间中不遵从右手定则,而是遵从左手定则

    opengl剪裁空间和视口空间中不遵从右手定则,而是遵从左手定则. 比如说要在视口空间判断一个三角形是否是正面朝向用户,就需要用左手定则而非右手定则.

  7. Java Web返回JSON

    Web项目中经常涉及到AJAX请求返回JSON和JSONP数据.JSON数据在server端和浏览器端传输,本质上就是传输字符串,只是这个字符串符合JSON语法格式.浏览器端会依照普通文本的格式接收J ...

  8. 设计模式之迪米特原则(LoD)

    迪米特原则也叫作最少知识原则,也就是:一个对象应该对其他对象有最少的了解.也就是说一个对象应该尽量的保证高内聚性,不应该对外有太多的public方法.

  9. C++语言基础(11)-多态

    一.产生背景 先看下面的例子: #include <iostream> using namespace std; //基类People class People{ public: Peop ...

  10. 试题系列四(袋中有6红球 3黄球 3绿球,从中取6个球,求所有拿到球的颜色的可能 c(12,6))

    1.袋中有6红球 3黄球 3绿球,从中取6个球,求所有拿到球的颜色的可能 c(12,6) #include<stdio.h> int main(int argc, char** argv) ...