Connections in Galaxy War
题目
假设有编号从0开始的n个点,每个点都有一个非负权值p[i]。现在有没有重边的m条边和Q个操作。
对于操作有两种类型
destroy a b 表示摧毁a,b点之间的边
query a 表示从a出发能到的点中,权值比a大权值最大,在权值最大前提下编号最小的点。如果没有这样的点输出-1。
分析
此题如果在线处理询问的话,不得不面对一个问题,也就是并查集的拆分,拆分后维护就变得异常困难。所以我们可以将问题存储在栈里面,先处理好并查集的最终状态,然后反向处理,也就是相当于是合并,这是并查集所擅长的。
有一点需要注意:map<pii,bool> 存储了a,b两个连接的边是否完好,需要将a,b按a小,b大统一一下,保持前后一致性。
AC代码
#include <iostream> #include <algorithm> #include <map> #include <stdio.h> #include <string> using namespace std; typedef pair<int,int> pii; const int maxn = 1e4+10; int N,M,Q; int power[maxn],fa[maxn]; struct info{ string op; int a,b; }sk[5*maxn];int top; map<pii,bool> mp; int res[5*maxn],tail; void init(){ top = 0;tail = 0; mp.clear(); for(int i = 0;i<N;i++) fa[i] = i; } int find(int x){ if(x!=fa[x]) fa[x] = find(fa[x]); return fa[x]; } void join(int x,int y){ int fx = find(x),fy = find(y); if(fx != fy){ //谁跟着谁看谁的能力大,其实是id小 if(power[fx] > power[fy]) fa[fy] = fx; else if(power[fy] > power[fx]) fa[fx] = fy; else if(fx<fy) fa[fy] = fx; else fa[fx] = fy; } } int main(){ int tag = 0; while(~scanf("%d",&N)){ if(tag) puts(""); tag = 1; init(); for(int i = 0;i<N;i++) scanf("%d",&power[i]); cin>>M; int a,b; while(M--){ scanf("%d %d",&a,&b); if(a>b) swap(a,b); mp[{a,b}] = true; } cin>>Q; string op; while (Q--){ ++top; cin>>op; if(op=="query"){ scanf("%d",&a); sk[top] = {op,a}; }else{ scanf("%d%d",&a,&b); if(a>b) swap(a,b); sk[top] = {op,a,b}; mp[{a,b}] = false;//摧毁边 } } for(const auto &m:mp){ //处理好并查集的最终状态 if(m.second){ int a = m.first.first,b = m.first.second; join(a,b); } } while(top){//反向处理 if(sk[top].op == "query"){ int t = find(sk[top].a); if(power[t] > power[sk[top].a]){ res[++tail] = t; }else{ res[++tail] = -1; } }else{ join(sk[top].a,sk[top].b); } top--; } for(int i = tail;i;i--){ //反向输出 printf("%d\n",res[i]); } } return 0; }