ZOJ 3261 (并查集+离线算法)

本题正方向求解十分困难。但倒过来就变成了普通问题。这就是离线算法的好处之一。

只是在联合不同节点时候要总以节点能量最大且编号尽量小的做根节点。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cctype>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
#define rep(i,n) for(int i = 0;i< (n);i++)
#define rep2(i,x,y) for(int (i) = (x) ; (i)< (y) ;(i)++)
const int maxn = 20101;
int p[maxn];
int find(int x){return p[x]==x ? x:p[x]=find(p[x]);}
int w[maxn],n,m,u[maxn],v[maxn],vis[maxn];
void Union(int x,int y){
  int px = find(x),py=find(y);
  if(px==py) return ;
  if(w[px]>w[py]||(w[px]==w[py]&&px<py)) p[py]=px;
  else  p[px]=py;
}
struct node{
  int cmd,p,x,y;
  node(int x=0,int y=0):x(x),y(y){}
  bool operator<(const node& a)const{
    return x<a.x||(x==a.x&&y<a.y);
  }
}st[50100];
map<node,int> id;
vector<int> ans;
int main()
{
    int kase=0;
    while(scanf("%d",&n)==1){
      if(kase++) printf("\n");
      rep(i,n){
        scanf("%d",&w[i]);
      }
      scanf("%d",&m);
      id.clear();
      rep(i,m){
       scanf("%d %d",&u[i],&v[i]);
       id[node(u[i],v[i])]=id[node(v[i],u[i])]=i;
      }
      memset(vis,0,sizeof(vis));
      int Q;
      scanf("%d",&Q);
      rep(i,Q){
         char cmd[10];
         int x,y;
         scanf("%s",&cmd);
         if(cmd[0]=='q'){
             scanf("%d",&x);
             st[i].cmd = 1;
             st[i].p = x;
         }
         else {
            scanf("%d %d",&x,&y);
            vis[id[node(x,y)]] = 1;
            st[i].cmd = 0;
            st[i].x=x,st[i].y=y;
         }
      }
      rep(i,n) p[i]=i;
      rep(i,m){
         if(!vis[i]){
             Union(u[i],v[i]);
         }
      }
      ans.clear();
      for(int i=Q-1;i>=0;i--){
          if(st[i].cmd==1){
             int px = find(st[i].p);
             if(w[px]<=w[st[i].p])
                 ans.push_back(-1);
             else ans.push_back(px);
          }
          else {
             Union(st[i].x,st[i].y);
          }
      }
      for(int i=ans.size()-1;i>=0;i--){
         printf("%d\n",ans[i]);
      }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值