数据结构(括号序列,线段树||点分治,堆):ZJOI 2007 捉迷藏
【题目描述】
Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子。某天,Jiajia、Wind和孩子们决定在家里玩捉迷藏游戏。他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达。
游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯。在起初的时候,所有的灯都没有被打开。每一次,孩子们只会躲 藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要求打开某个房间的电灯或者关闭某个房间的电灯。为了评估某一次游戏的复杂性,Jiajia希望知道 可能的最远的两个孩子的距离(即最远的两个关灯房间的距离)。
我们将以如下形式定义每一种操作:
C(hange) i 改变第i个房间的照明状态,若原来打开,则关闭;若原来关闭,则打开。
G(ame) 开始一次游戏,查询最远的两个关灯房间的距离。
【输入格式】
第一行包含一个整数N,表示房间的个数,房间将被编号为1,2,3…N的整数。接下来N-1行每行两个整数a, b,表示房间a与房间b之间有一条走廊相连。接下来一行包含一个整数Q,表示操作次数。接着Q行,每行一个操作,如上文所示。
【输出格式】
对于每一个操作Game,输出一个非负整数到hide.out,表示最远的两个关灯房间的距离。若只有一个房间是关着灯的,输出0;若所有房间的灯都开着,输出-1。
【样例输入】
8
1 2
2 3
3 4
3 5
3 6
6 7
6 8
7
G
C 1
G
C 2
G
C 1
G
【样例输出】
4
3
3
4
【提示】
对于20%的数据, N ≤50, M ≤100;
对于60%的数据, N ≤3000, M ≤10000;
对于100%的数据, N ≤100000, M ≤500000。
这道题有三种做法。
我这里用线段树维护了一个括号序列。
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int maxn=100010; 6 const int INF=1000000000; 7 int cnt,fir[maxn],nxt[maxn*2],to[maxn*2]; 8 void addedge(int a,int b){ 9 nxt[++cnt]=fir[a]; 10 fir[a]=cnt; 11 to[cnt]=b; 12 } 13 int c[maxn]; 14 int ID[maxn],rID[maxn*3],tot; 15 struct Node{ 16 int a,b,l1,l2,r1,r2,dis; 17 void Init(int p){ 18 dis=-INF;a=b=0; 19 if(rID[p]==-2)b=1; 20 if(rID[p]==-3)a=1; 21 if(rID[p]>0&&c[rID[p]]) 22 l1=l2=r1=r2=0; 23 else 24 l1=l2=r1=r2=-INF; 25 } 26 void Push_up(Node l,Node r){ 27 int a1=l.a,b1=l.b,a2=r.a,b2=r.b; 28 if(b1>=a2)a=a1,b=b1+b2-a2; 29 else a=a1+a2-b1,b=b2; 30 31 dis=max(l.dis,r.dis); 32 dis=max(dis,max(l.r1+r.l2,l.r2+r.l1)); 33 34 r1=max(r.r1,max(l.r1+b2-a2,l.r2+a2+b2)); 35 r2=max(r.r2,l.r2+a2-b2); 36 37 l1=max(l.l1,max(a1-b1+r.l1,a1+b1+r.l2)); 38 l2=max(l.l2,r.l2+b1-a1); 39 } 40 }tr[(maxn*3)<<2]; 41 42 void DFS(int x,int fa){ 43 rID[++tot]=-2; 44 rID[++tot]=x; 45 ID[x]=tot; 46 47 for(int i=fir[x];i;i=nxt[i]) 48 if(to[i]!=fa) 49 DFS(to[i],x); 50 rID[++tot]=-3; 51 } 52 53 void Build(int x,int l,int r){ 54 if(l==r){ 55 tr[x].Init(l); 56 57 return; 58 } 59 int mid=(l+r)>>1; 60 Build(x<<1,l,mid); 61 Build(x<<1|1,mid+1,r); 62 tr[x].Push_up(tr[x<<1],tr[x<<1|1]); 63 } 64 65 void Modify(int x,int l,int r,int g){ 66 if(l==r){ 67 tr[x].Init(l); 68 return; 69 } 70 int mid=(l+r)>>1; 71 if(mid>=g)Modify(x<<1,l,mid,g); 72 else Modify(x<<1|1,mid+1,r,g); 73 tr[x].Push_up(tr[x<<1],tr[x<<1|1]); 74 } 75 int n,Q,x; 76 char op[10]; 77 int main(){ 78 #ifndef ONLINE_JUDGE 79 freopen("hide.in","r",stdin); 80 freopen("hide.out","w",stdout); 81 #endif 82 scanf("%d",&n); 83 for(int i=1;i<=n;i++)c[i]=1; 84 for(int i=1,a,b;i<n;i++){ 85 scanf("%d%d",&a,&b); 86 addedge(a,b); 87 addedge(b,a); 88 } 89 90 DFS(1,1); 91 Build(1,1,tot); 92 93 scanf("%d",&Q); 94 while(Q--){ 95 scanf("%s",op); 96 if(op[0]=='C'){ 97 scanf("%d",&x); 98 (c[x])?n--:n++;c[x]^=1; 99 Modify(1,1,tot,ID[x]); 100 } 101 else{ 102 if(n==0)printf("-1\n"); 103 else if(n==1)printf("0\n"); 104 else printf("%d\n",tr[1].dis); 105 } 106 } 107 return 0; 108 }
然后我又用点分治+堆的方法AC了一遍。
先按点分治访问 rt 节点的顺序建 fa 边,每次改一个点只要延 fa 边一路改上去就可以了。
关于开三个堆:
A:最终的答案。
B:对于每个点,都有一个对应的B类堆,B中最多只有此节点的子节点个数的元素,B中元素为该点子节点的C堆的堆顶。
C:对于每个点,都有一个对应的C类堆,堆中记录的是每一个子树中的节点对此点的 fa 的贡献。
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <queue> 5 using namespace std; 6 const int maxn=100010; 7 8 struct Heap{ 9 priority_queue<int>A,B; 10 void Insert(int x){A.push(x);} 11 void Delete(int x){B.push(x);} 12 void Pop(){ 13 while(B.size()&&A.top()==B.top()) 14 A.pop(),B.pop(); 15 A.pop(); 16 } 17 int Size(){return A.size()-B.size();} 18 int Max(){ 19 while(B.size()&&A.top()==B.top()) 20 A.pop(),B.pop(); 21 if(!A.size())return 0; 22 return A.top(); 23 } 24 int Max2(){ 25 if(Size()<2)return 0; 26 int p=Max(),ret;Pop();ret=Max(); 27 Insert(p);return ret; 28 } 29 }A,B[maxn],C[maxn]; 30 31 int fir[maxn],nxt[maxn*2],to[maxn*2],cnt; 32 void addedge(int a,int b){nxt[++cnt]=fir[a];to[cnt]=b;fir[a]=cnt;} 33 34 int tot,ID[maxn],dep[maxn]; 35 int Min[maxn*3][20],mm[maxn*3]; 36 void DFS(int x,int fa){ 37 Min[ID[x]=++tot][0]=dep[x]; 38 for(int i=fir[x];i;i=nxt[i]) 39 if(to[i]!=fa){ 40 dep[to[i]]=dep[x]+1; 41 DFS(to[i],x); 42 Min[++tot][0]=dep[x]; 43 } 44 } 45 46 int Dis(int x,int y){ 47 if(ID[x]>ID[y])swap(x,y); 48 int k=mm[ID[y]-ID[x]+1]; 49 int ret=min(Min[ID[x]][k],Min[ID[y]-(1<<k)+1][k]); 50 return dep[x]+dep[y]-2*ret; 51 } 52 53 bool vis[maxn]; 54 int sz[maxn],son[maxn],rt,N; 55 void Get_RT(int x,int fa){ 56 sz[x]=1;son[x]=0; 57 for(int i=fir[x];i;i=nxt[i]) 58 if(!vis[to[i]]&&to[i]!=fa){ 59 Get_RT(to[i],x); 60 sz[x]+=sz[to[i]]; 61 son[x]=max(son[x],sz[to[i]]); 62 } 63 son[x]=max(son[x],N-sz[x]); 64 if(!rt||son[rt]>son[x])rt=x; 65 } 66 67 int fa[maxn]; 68 void Div(int x,int f){ 69 vis[x]=true;fa[x]=f; 70 for(int i=fir[x];i;i=nxt[i]) 71 if(!vis[to[i]]){ 72 rt=0;N=sz[to[i]]; 73 Get_RT(to[i],0); 74 Div(rt,x); 75 } 76 } 77 78 int light[maxn]; 79 void Turn_OFF(int p,int x){ 80 if(p==x){ 81 B[x].Insert(0); 82 if(B[x].Size()==2) 83 A.Insert(B[x].Max()); 84 } 85 if(!fa[p])return; 86 int f=fa[p],mx=C[p].Max(),dis=Dis(f,x); 87 C[p].Insert(dis); 88 if(mx<dis){ 89 int mt=B[f].Max()+B[f].Max2(),sz=B[f].Size(); 90 if(C[p].Size()!=1) 91 B[f].Delete(mx); 92 93 B[f].Insert(dis); 94 if(mt<B[f].Max()+B[f].Max2()){ 95 if(sz>=2)A.Delete(mt); 96 if(B[f].Size()>=2)A.Insert(B[f].Max()+B[f].Max2()); 97 } 98 } 99 Turn_OFF(f,x); 100 } 101 102 void Turn_ON(int p,int x){ 103 if(p==x){ 104 B[p].Delete(0); 105 if(B[p].Size()==1) 106 A.Delete(B[p].Max()); 107 } 108 if(!fa[p])return; 109 int f=fa[p],dis=Dis(f,x),mx; 110 C[p].Delete(dis);mx=C[p].Max(); 111 if(mx<dis){ 112 int mt=B[f].Max()+B[f].Max2(),sz=B[f].Size(); 113 B[f].Delete(dis); 114 if(C[p].Size()) 115 B[f].Insert(mx); 116 if(sz>=2&&mt>B[f].Max()+B[f].Max2()){ 117 if(sz>=2)A.Delete(mt); 118 if(B[f].Size()>=2)A.Insert(B[f].Max()+B[f].Max2()); 119 } 120 } 121 Turn_ON(fa[p],x); 122 } 123 124 int main(){ 125 #ifndef ONLINE_JUDGE 126 freopen("hide.in","r",stdin); 127 freopen("hide.out","w",stdout); 128 #endif 129 int n; 130 scanf("%d",&n); 131 for(int i=1,a,b;i<n;i++){ 132 scanf("%d%d",&a,&b); 133 addedge(a,b); 134 addedge(b,a); 135 } 136 DFS(1,1);mm[0]=-1; 137 for(int i=1;i<=tot;i++){ 138 if((i&(i-1))==0)mm[i]=mm[i-1]+1; 139 else mm[i]=mm[i-1]; 140 } 141 for(int k=1;k<=mm[tot];k++) 142 for(int i=1;i+(1<<k)-1<=tot;i++) 143 Min[i][k]=min(Min[i][k-1],Min[i+(1<<(k-1))][k-1]); 144 N=n;rt=0; 145 Get_RT(1,0); 146 Div(rt,0); 147 148 for(int i=1;i<=n;i++) 149 Turn_OFF(i,i); 150 151 char op[5]; 152 int Q,tot=n,x; 153 scanf("%d",&Q); 154 while(Q--){ 155 scanf("%s",op); 156 if(op[0]=='C'){ 157 scanf("%d",&x); 158 if(light[x])Turn_OFF(x,x); 159 else Turn_ON(x,x); 160 light[x]^=1; 161 } 162 else{ 163 if(tot<2) 164 printf("%d\n",tot-1); 165 else 166 printf("%d\n",A.Max()); 167 } 168 } 169 return 0; 170 }
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· C# 13 中的新增功能实操
· Ollama本地部署大模型总结
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(4)
· langchain0.3教程:从0到1打造一个智能聊天机器人
· 2025成都.NET开发者Connect圆满结束