ZOJ - 3261
题意
这道并查集很有意思,一般的并查集都是加边,但它却是删边
当然啦,要是删边那还怎么用并查集来做呢?所以我们这里就要逆向思考,既然不会做删边,那就反着做加边
要反着做,那当然就不能一边输入一边处理了,所以要离线处理
(离线处理指当所有的数据输入结束后再开始处理)
代码附上:
#pragma GCC optimize("Ofast","inline","-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4+10;
int n,m,q,a[N];
int fa[N];
struct node//需要连接的点的数据
{
int c,d;
} link[N<<1];
struct query//查询的数据
{
string ss;
int cc,dd;
} qq[50010];
void init()//初始化
{
for(int i=0; i<=n; ++i)
fa[i]=i;
}
int fd_ys(int k)//合并压缩
{
if(k!=fa[k])
fa[k]=fd_ys(fa[k]);
return fa[k];
}
void work(int c,int d)//点之间的处理
{
int C=fd_ys(c);
int D=fd_ys(d);
if(C==D)
return ;
if(a[C]==a[D])
{
if(C<D)
swap(C,D);
fa[C]=D;
return ;
}
if(a[C]>a[D])
swap(C,D);
fa[C]=D;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
bool cnt=0;
while(cin>>n)
{
map<pair<int,int>,int>vis;//检查点与点能否连接
stack<int>ans;
init();
for(int i=0; i<n; ++i)//每个点的值
cin>>a[i];
cin>>m;
for(int i=0; i<m; ++i)//需要连接的每对点
cin>>link[i].c>>link[i].d;
cin>>q;
for(int i=0; i<q; ++i)//查询
{
cin>>qq[i].ss;
if(qq[i].ss=="query")
{
cin>>qq[i].cc;
continue;
}
cin>>qq[i].cc>>qq[i].dd;
vis[make_pair(qq[i].cc,qq[i].dd)]=1;//标记被破坏的每对点
vis[make_pair(qq[i].dd,qq[i].cc)]=1;
}
for(int i=0; i<m; ++i)//除去被破坏的点,点与点之间连接
{
if(vis[make_pair(link[i].c,link[i].d)])
continue;
else
work(link[i].c,link[i].d);
}
for(int i=q-1; i>=0; --i)
{
if(qq[i].ss=="query")//查询
{
fd_ys(qq[i].cc);
if(a[fa[qq[i].cc]]==a[qq[i].cc])
ans.push(-1);
else
ans.push(fa[qq[i].cc]);
}
else
work(qq[i].cc,qq[i].dd);//连接
}
if(cnt)
cout<<endl;//两组测试数据之间输出一个空行
else
cnt=1;
while(ans.size())
{
cout<<ans.top()<<endl;//输出答案
ans.pop();
}
}
return 0;
}