• BZOJ2325: [ZJOI2011]道馆之战


    BZOJ2325: [ZJOI2011]道馆之战

    某$ZOJ$上竟然是个权限题。。。

    没钱氪金的穷$AFO$狗老泪纵横。。。

    附上大美洛谷的题面:

    P4679 [ZJOI2011]道馆之战

    题目描述

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

    当一个冰地上的所有冰块都被经过之后,到下一个冰地的楼梯才会被打开。

    三个冰地分别如下:

    • 第一个冰地:

     

    • 第二个冰地:

     

    • 第三个冰地:

    当走出第三个冰地之后,就可以与馆主进行道馆战了。

    馆主发现这个难度太小,导致经常有挑战者能通过,为了加大难度,将道馆分成了n个房间,任意两个房间之间均有且仅有一条路径相连,即这n个房间构成一个树状结构。

    每个房间分成了AB两个区域,每一区域都是一个薄冰块或者障碍物。

    每次只能移动到相邻房间的同一类区域(即若你现在在这个房间的A区域,那么你只能移动到相邻房间的AA区域)或这个房间的另一区域。

    现在挑战者从房间u出发,馆主在房间v,那么挑战者只能朝接近馆主所在房间的方向过去。

    一开始挑战者可以在房间u的任意一个冰块区域内。

    如果挑战者踩过的冰块数达到了最大值(即没有一种方案踩过的冰块数更多了),那么当挑战者走到最后一个冰块上时,他会被瞬间传送到馆主面前与馆主进行道馆战。

    自从馆主修改规则后已经经过了m天,每天要么是有一个挑战者来进行挑战,要么就是馆主将某个房间进行了修改。

    对于每个来的挑战者,你需要计算出他若要和馆主进行战斗需要经过的冰块数。

    输入输出格式

    输入格式:

    第一行包含两个正整数nm

    2行到第n行,每行包含两个正整数xy,表示一条连接房间x和房间y的边。房间编号为1n。

    接下来n行,每行包含两个字符。第n+k行表示房间k的两个区域,第一个字符为A区域,第二个字符为B区域。

    其中“.”(ASCII码为46)表示是薄冰块,“#”(ASCII码为35)表示是障碍物。

    最后的m行,每行一个操作:

    C u s:将房间u里的两个区域修改为s

    Q u v:询问挑战者在房间u,馆主在房间v时,挑战者能与馆主进行挑战需要踩的冰块数。

    如果房间u的两个区域都是障碍物,那么输出0

    输出格式:

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

    输入输出样例

    输入样例: 
    5 3
    1 2
    2 3
    2 4
    1 5
    .#
    ..
    #.
    .#
    ..
    Q 5 3
    C 1 ##
    Q 4 5
    
    输出样例: 
    6
    3

    说明

    测试点1~6:n≤1000,m≤10000

    测试点7~15n≤30000,m≤80000

    测试点16~20n≤50000,m≤100000


    题解Here!

    这个题的题目描述烂到家了。。。

    本蒟蒻看了至少半个小时,翻了至少$6$篇题解,才看懂题面。。。

    题意补充戳这

    首先树上问题一发树剖转成链上问题没得说。

    在当前区间中,我们发现其实就是一个$n imes 2$的矩阵。

    感觉就是把堵塞的交通的图顺时针转了$90$。。。

    设:

    $data[0][0]$表示从左上走到右上的最长路,$data[0][1]$表示从左上到右下,$data[1][0],data[1][1]$以此类推。

    $maxd[0][0]$表示从左上出发能走的最大距离,$maxd[0][1]$表示左下的,$maxd[1][0]$表示右上,$maxd[1][1]$表示右下。

    然后就是丧心病狂的$pushup$(为了方便我写成了结构体):

    friend Segment_Tree operator +(const Segment_Tree &p,const Segment_Tree &q){
    	if(empty(p))return q;
    	if(empty(q))return p;
    	Segment_Tree x;
    	//--------------------------------------------------------------------------------
    	x.data[0][0]=max(-MAX,max(p.data[0][0]+q.data[0][0],p.data[0][1]+q.data[1][0]));
    	x.data[0][1]=max(-MAX,max(p.data[0][0]+q.data[0][1],p.data[0][1]+q.data[1][1]));
    	x.data[1][0]=max(-MAX,max(p.data[1][1]+q.data[1][0],p.data[1][0]+q.data[0][0]));
    	x.data[1][1]=max(-MAX,max(p.data[1][1]+q.data[1][1],p.data[1][0]+q.data[0][1]));
    	//--------------------------------------------------------------------------------
    	x.maxd[0][0]=max(p.maxd[0][0],max(p.data[0][0]+q.maxd[0][0],p.data[0][1]+q.maxd[0][1]));
    	x.maxd[0][1]=max(p.maxd[0][1],max(p.data[1][0]+q.maxd[0][0],p.data[1][1]+q.maxd[0][1]));
    	x.maxd[1][0]=max(q.maxd[1][0],max(q.data[0][0]+p.maxd[1][0],q.data[1][0]+p.maxd[1][1]));
    	x.maxd[1][1]=max(q.maxd[1][1],max(q.data[0][1]+p.maxd[1][0],q.data[1][1]+p.maxd[1][1]));
    	//--------------------------------------------------------------------------------
    	x.l=p.l;x.r=q.r;
    	return x;
    }

    简直了。。。我也没有什么办法。。。出题人大$duliu$。。。

    接下来就是树剖的合并了。

    我们注意到这其实是两条链:$u->LCA(u,v),LCA(u,v)->v$

    并且这两条链得分开维护。

    为什么?

    因为这玩意的有序性。。。

    而且就算知道了这个也不行,还要把$u->LCA(u,v)$反向。

    同样是因为这玩意的有序性。。。

    然后我就因为边界问题调了半小时。。。

    $AFO$后码力极度下降。。。

    附上奇丑无比的代码:(198行。。。强迫症患者很难受。。。)

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #define LSON rt<<1
    #define RSON rt<<1|1
    #define LSIDE(x) b[x].l
    #define RSIDE(x) b[x].r
    #define MAXN 50010
    #define MAX (1<<30)
    using namespace std;
    int n,m,c=1,d=1;
    int val[MAXN][2],head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],id[MAXN],pos[MAXN],top[MAXN];
    struct Edge{
    	int next,to;
    }a[MAXN<<1];
    struct Segment_Tree{
    	int data[2][2],maxd[2][2],l,r;
    	void clean(){
    		memset(data,0,sizeof(data));
    		memset(maxd,0,sizeof(maxd));
    	}
    	void reverse(){
    		swap(data[0][1],data[1][0]);
    		swap(maxd[0][0],maxd[1][0]);
    		swap(maxd[0][1],maxd[1][1]);
    	}
    	friend bool empty(const Segment_Tree &x){
    		for(int i=0;i<=1;i++)for(int j=0;j<=1;j++)if(x.data[i][j]||x.maxd[i][j])return false;
    		return true;
    	}
    	void value(const int &p,const int &q){
    		if(p==1&&q==1){
    			data[0][0]=data[1][1]=1;
    			data[0][1]=data[1][0]=2;
    			maxd[0][0]=maxd[0][1]=maxd[1][0]=maxd[1][1]=2;
    		}
    		else if(p==1){
    			data[0][0]=1;
    			data[0][1]=data[1][0]=data[1][1]=-MAX;
    			maxd[0][0]=maxd[1][0]=1;
    			maxd[0][1]=maxd[1][1]=-MAX;
    		}
    		else if(q==1){
    			data[0][0]=data[0][1]=data[1][0]=-MAX;
    			data[1][1]=1;
    			maxd[0][0]=maxd[1][0]=-MAX;
    			maxd[0][1]=maxd[1][1]=1;
    		}
    		else{
    			data[0][0]=data[0][1]=data[1][0]=data[1][1]=-MAX;
    			maxd[0][0]=maxd[0][1]=maxd[1][0]=maxd[1][1]=-MAX;
    		}
    	}
    	friend Segment_Tree operator +(const Segment_Tree &p,const Segment_Tree &q){
    		if(empty(p))return q;
    		if(empty(q))return p;
    		Segment_Tree x;
    		//--------------------------------------------------------------------------------
    		x.data[0][0]=max(-MAX,max(p.data[0][0]+q.data[0][0],p.data[0][1]+q.data[1][0]));
    		x.data[0][1]=max(-MAX,max(p.data[0][0]+q.data[0][1],p.data[0][1]+q.data[1][1]));
    		x.data[1][0]=max(-MAX,max(p.data[1][1]+q.data[1][0],p.data[1][0]+q.data[0][0]));
    		x.data[1][1]=max(-MAX,max(p.data[1][1]+q.data[1][1],p.data[1][0]+q.data[0][1]));
    		//--------------------------------------------------------------------------------
    		x.maxd[0][0]=max(p.maxd[0][0],max(p.data[0][0]+q.maxd[0][0],p.data[0][1]+q.maxd[0][1]));
    		x.maxd[0][1]=max(p.maxd[0][1],max(p.data[1][0]+q.maxd[0][0],p.data[1][1]+q.maxd[0][1]));
    		x.maxd[1][0]=max(q.maxd[1][0],max(q.data[0][0]+p.maxd[1][0],q.data[1][0]+p.maxd[1][1]));
    		x.maxd[1][1]=max(q.maxd[1][1],max(q.data[0][1]+p.maxd[1][0],q.data[1][1]+p.maxd[1][1]));
    		//--------------------------------------------------------------------------------
    		x.l=p.l;x.r=q.r;
    		return x;
    	}
    }b[MAXN<<2];
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    inline void add(int x,int y){
    	a[c].to=y;a[c].next=head[x];head[x]=c++;
    	a[c].to=x;a[c].next=head[y];head[y]=c++;
    }
    void dfs1(int rt){
    	son[rt]=0;size[rt]=1;
    	for(int i=head[rt];i;i=a[i].next){
    		int will=a[i].to;
    		if(!deep[will]){
    			deep[will]=deep[rt]+1;
    			fa[will]=rt;
    			dfs1(will);
    			size[rt]+=size[will];
    			if(size[will]>size[son[rt]])son[rt]=will;
    		}
    	}
    }
    void dfs2(int rt,int f){
    	id[rt]=d++;pos[id[rt]]=rt;top[rt]=f;
    	if(son[rt])dfs2(son[rt],f);
    	for(int i=head[rt];i;i=a[i].next){
    		int will=a[i].to;
    		if(will!=son[rt]&&will!=fa[rt])dfs2(will,will);
    	}
    }
    inline void pushup(int rt){
    	b[rt]=b[LSON]+b[RSON];
    }
    void buildtree(int l,int r,int rt){
    	LSIDE(rt)=l;RSIDE(rt)=r;
    	if(l==r){
    		b[rt].value(val[pos[l]][0],val[pos[l]][1]);
    		return;
    	}
    	int mid=l+r>>1;
    	buildtree(l,mid,LSON);
    	buildtree(mid+1,r,RSON);
    	pushup(rt);
    }
    void update(int k,int p,int q,int rt){
    	if(LSIDE(rt)==RSIDE(rt)){
    		b[rt].value(p,q);
    		return;
    	}
    	int mid=LSIDE(rt)+RSIDE(rt)>>1;
    	if(k<=mid)update(k,p,q,LSON);
    	else update(k,p,q,RSON);
    	pushup(rt);
    }
    Segment_Tree query(int l,int r,int rt){
    	if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return b[rt];
    	int mid=LSIDE(rt)+RSIDE(rt)>>1;
    	Segment_Tree ans;
    	ans.clean();
    	if(l<=mid)ans=ans+query(l,r,LSON);
    	if(mid<r)ans=ans+query(l,r,RSON);
    	return ans;
    }
    int query_path(int x,int y){
    	Segment_Tree left,right,ans;
    	left.clean();
    	right.clean();
    	while(top[x]!=top[y]){
    		if(deep[top[x]]<deep[top[y]]){
    			right=query(id[top[y]],id[y],1)+right;
    			y=fa[top[y]];
    		}
    		else{
    			left=query(id[top[x]],id[x],1)+left;
    			x=fa[top[x]];
    		}
    	}
    	if(deep[x]<=deep[y])right=query(id[x],id[y],1)+right;
    	else left=query(id[y],id[x],1)+left;
    	left.reverse();
    	ans=left+right;
    	return max(ans.maxd[0][0],ans.maxd[0][1]);
    }
    void work(){
    	char ch[4];
    	int x,y;
    	while(m--){
    		scanf("%s",ch);x=read();
    		if(ch[0]=='C'){
    			scanf("%s",ch);
    			val[x][0]=(ch[0]=='.'?1:0);
    			val[x][1]=(ch[1]=='.'?1:0);
    			update(id[x],val[x][0],val[x][1],1);
    		}
    		else{
    			y=read();
    			if(val[x][0]==0&&val[x][1]==0)printf("0
    ");
    			else printf("%d
    ",query_path(x,y));
    		}
    	}
    }
    void init(){
    	int x,y;
    	char ch[4];
    	n=read();m=read();
    	for(int i=1;i<n;i++){
    		x=read();y=read();
    		add(x,y);
    	}
    	deep[1]=1;
    	dfs1(1);
    	dfs2(1,1);
    	for(int i=1;i<=n;i++){
    		scanf("%s",ch);
    		val[i][0]=(ch[0]=='.'?1:0);
    		val[i][1]=(ch[1]=='.'?1:0);
    	}
    	buildtree(1,n,1);
    }
    int main(){
    	init();
    	work();
    	return 0;
    }
  • 相关阅读:
    org.apache.maven.archiver.MavenArchiver.getManifest
    网易云信发送短信验证码
    background-attachment:fixed;
    background-size属性100% cover contain
    width:100% width:auto 区别
    背景图全屏显示
    多行文字的垂直居中
    路径问题../绝对路径/
    用position子绝父相和css3动画,当鼠标一上去元素从底部慢慢向上
    前置后置运算符重载
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/10386231.html
Copyright © 2020-2023  润新知