TJ - 「ZJOI2011」道馆之战
「ZJOI2011」道馆之战
难度:2500
一,题目:
题目大意:
给你一颗
数据范围:
测试点
测试点
测试点
读入格式:
第一行为
第
接下来
最后的
样例输入:
5 3 1 2 2 3 2 4 1 5 .# .. #. .# .. Q 5 3 C 1 ## Q 4 5
样例输出:
6 3
二,solution:
首先,这题一眼就是树剖,但是,与一般的题目不同的是,一个节点被分为了两个房间,那么树剖后线段树怎么搞呢?
那么分类讨论就可以了嘛。
我们钦定一个房间编号为
我们树剖后,对于线段树,我们维护这些信息:
首先,新建一个节点的时候好办,直接分类讨论即可:
void make(node &k,int x){//新建一个节点: //赋初始值 memset(k.lans,0,sizeof(k.lans)); memset(k.rans,0,sizeof(k.rans)); memset(k.dis,-0x3f,sizeof(k.dis)); k.l=k.r=x;//表示这个区间的范围 x=rev[x]; if(a[x][0]==0&&a[x][1]==0) return;//左右房间都是障碍物 else if(a[x][0]==0&&a[x][1]==1){//左房间:障碍物;右房间:冰块 k.lans[1]=k.rans[1]=1; k.dis[1][1]=1; } else if(a[x][0]==1&&a[x][1]==1){//左右房间都是冰块 k.lans[1]=k.rans[1]=k.lans[0]=k.rans[0]=2; k.dis[0][0]=k.dis[1][1]=1; k.dis[0][1]=k.dis[1][0]=2; } else{//左房间:冰块;右房间:障碍物 k.lans[0]=k.rans[0]=1; k.dis[0][0]=1; } }
合并两个区间要麻烦一点,对于其实还是枚举):
node add(node L,node R){//合并L,R子树 node ans; //赋初始值 memset(ans.lans,0,sizeof(ans.lans)); memset(ans.rans,0,sizeof(ans.rans)); memset(ans.dis,-0x3f,sizeof(ans.dis)); ans.l=L.l,ans.r=R.r; for(int i=0;i<=1;i++){ for(int j=0;j<=1;j++){ ans.lans[i]=max(ans.lans[i],max(L.lans[i],L.dis[i][j]+R.lans[j])); ans.rans[i]=max(ans.rans[i],max(R.rans[i],R.dis[j][i]+L.rans[j])); for(int k=0;k<=1;k++) ans.dis[i][j]=max(ans.dis[i][j],L.dis[i][k]+R.dis[k][j]); } } return ans; }
对于修改操作,我们每次直接线段树单点修改,和建树时一样,调用一下
但是,对于查询操作,就比较恶心的。
首先,这道题是规定了方向的,是从
我们查询的时候分类讨论,设
所以,我们需要把
swap(ans1.lans[0],ans1.rans[0]); swap(ans1.lans[1],ans1.rans[1]); swap(ans1.dis[1][0],ans1.dis[0][1]);
然后把
查询的代码:
int solve_2(){//路径查询 int x,y,fx,fy; scanf("%d%d",&x,&y); node ans1,ans2; clean(ans1);clean(ans2); fx=top[x],fy=top[y]; while(fx!=fy){ if(deep[fx]>deep[fy]){ ans1=add(ask(1,id[fx],id[x]),ans1); x=fa[fx];fx=top[x]; } else{ ans2=add(ask(1,id[fy],id[y]),ans2); y=fa[fy];fy=top[y]; } } if(deep[x]>deep[y]){ ans1=add(ask(1,id[y],id[x]),ans1); } else{ ans2=add(ask(1,id[x],id[y]),ans2); } swap(ans1.lans[0],ans1.rans[0]); swap(ans1.lans[1],ans1.rans[1]); swap(ans1.dis[1][0],ans1.dis[0][1]); ans1=add(ans1,ans2); return max(ans1.lans[0],ans1.lans[1]); }
代码:
#include<bits/stdc++.h> using namespace std; const int N=50005,INF=0x3f3f3f3f; int n,m; //树剖: vector<int> G[N]; int son[N],siz[N],fa[N],deep[N]; int id[N],rev[N],top[N],dfn; void dfs1(int x,int dad){ siz[x]=1,fa[x]=dad,deep[x]=deep[dad]+1; for(auto y : G[x]){ if(y==dad) continue; dfs1(y,x); if(siz[son[x]]<siz[y]) son[x]=y; siz[x]+=siz[y]; } } void dfs2(int x){ id[x]=++dfn;rev[dfn]=x; if(son[x]){ top[son[x]]=top[x]; dfs2(son[x]); } for(auto y : G[x]){ if(id[y]) continue; top[y]=y; dfs2(y); } } int a[N][2]; struct node{ int l,r; int lans[2],rans[2],dis[2][2]; }tree[N*4]; void clean(node &x){ memset(x.lans,0,sizeof(x.lans)); memset(x.rans,0,sizeof(x.rans)); memset(x.dis,0,sizeof(x.dis)); } node add(node L,node R){//合并L,R子树 node ans; memset(ans.lans,0,sizeof(ans.lans)); memset(ans.rans,0,sizeof(ans.rans)); memset(ans.dis,-0x3f,sizeof(ans.dis)); ans.l=L.l,ans.r=R.r; for(int i=0;i<=1;i++){ for(int j=0;j<=1;j++){ ans.lans[i]=max(ans.lans[i],max(L.lans[i],L.dis[i][j]+R.lans[j])); ans.rans[i]=max(ans.rans[i],max(R.rans[i],R.dis[j][i]+L.rans[j])); for(int k=0;k<=1;k++) ans.dis[i][j]=max(ans.dis[i][j],L.dis[i][k]+R.dis[k][j]); } } return ans; } void make(node &k,int x){//新建一个节点: memset(k.lans,0,sizeof(k.lans)); memset(k.rans,0,sizeof(k.rans)); memset(k.dis,-0x3f,sizeof(k.dis)); k.l=k.r=x; x=rev[x]; if(a[x][0]==0&&a[x][1]==0) return; else if(a[x][0]==0&&a[x][1]==1){ k.lans[1]=k.rans[1]=1; k.dis[1][1]=1; } else if(a[x][0]==1&&a[x][1]==1){ k.lans[1]=k.rans[1]=k.lans[0]=k.rans[0]=2; k.dis[0][0]=k.dis[1][1]=1; k.dis[0][1]=k.dis[1][0]=2; } else{ k.lans[0]=k.rans[0]=1; k.dis[0][0]=1; } } //线段树模板: void build(int k,int l,int r){ if(l==r){ make(tree[k],l); tree[k].l=l,tree[k].r=r; return; } int mid=(l+r)>>1; build(k<<1,l,mid);build(k<<1|1,mid+1,r); tree[k]=add(tree[k<<1],tree[k<<1|1]); } void change(int k,int x){ if(tree[k].l==tree[k].r&&tree[k].l==x){ make(tree[k],x); return; } if(x<=tree[k<<1].r) change(k<<1,x); else change(k<<1|1,x); tree[k]=add(tree[k<<1],tree[k<<1|1]); } node ask(int k,int x,int y){ if(x<=tree[k].l&&tree[k].r<=y) return tree[k]; if(y<=tree[k<<1].r) return ask(k<<1,x,y); if(x>=tree[k<<1|1].l) return ask(k<<1|1,x,y); return add(ask(k<<1,x,y),ask(k<<1|1,x,y)); } void solve_1(){//单点修改 int x;char ch1,ch2; cin>>x>>ch1>>ch2; a[x][0]=(ch1=='.'); a[x][1]=(ch2=='.'); change(1,id[x]); } int solve_2(){//路径查询 int x,y,fx,fy; scanf("%d%d",&x,&y); node ans1,ans2; //ans1表示从x<-lca(x,y),ans2表示y<-lca(x,y),一开始他们都为0 clean(ans1);clean(ans2); fx=top[x],fy=top[y]; //由于有方向上的区别,需要分类讨论: while(fx!=fy){ if(deep[fx]>deep[fy]){ ans1=add(ask(1,id[fx],id[x]),ans1); x=fa[fx];fx=top[x]; } else{ ans2=add(ask(1,id[fy],id[y]),ans2); y=fa[fy];fy=top[y]; } } if(deep[x]>deep[y]){ ans1=add(ask(1,id[y],id[x]),ans1); } else{ ans2=add(ask(1,id[x],id[y]),ans2); } //交换,使得ans1表示x->lca(x,y) swap(ans1.lans[0],ans1.rans[0]); swap(ans1.lans[1],ans1.rans[1]); swap(ans1.dis[1][0],ans1.dis[0][1]); ans1=add(ans1,ans2);//合并 return max(ans1.lans[0],ans1.lans[1]); } int main(){ scanf("%d%d",&n,&m); int x,y; for(int i=1;i<n;i++){ scanf("%d%d",&x,&y); G[x].push_back(y); G[y].push_back(x); } //树剖: dfs1(1,0); top[1]=1; dfs2(1); char ch1,ch2; for(int i=1;i<=n;i++){ cin>>ch1>>ch2; a[i][0]=(ch1=='.'); a[i][1]=(ch2=='.'); } //建立线段树: build(1,1,n); char opt; while(m--){ cin>>opt; if(opt=='C') solve_1(); else printf("%d\n",solve_2()); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】