[最小极差生成树 LCT || 二分答案 CDQ分治 并查集] Ural 2055 Urban Geography

把边按权值大小排序
然后用LCT按时间维护下最大生成树
就好了

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<stack>
#include<set>
using namespace std;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=100005;

int u[N],v[N],w[N];
inline int Min(int a,int b){
  return w[a]<w[b]?a:b;
}
struct node{
  node *ch[2],*f;
  int v,idx; bool rev;
  bool isr() { return !f || (f->ch[0]!=this && f->ch[1]!=this); }
  bool dir() { return f->ch[1]==this; }
  void setc(node *c,int d){ ch[d]=c; if (c) c->f=this; }
  void push(){
    if (rev) { swap(ch[0],ch[1]); if(ch[0])ch[0]->rev^=1; if(ch[1])ch[1]->rev^=1; rev=0; }
  }
  void upd(){
    v=idx; if(ch[0])v=Min(v,ch[0]->v); if(ch[1])v=Min(v,ch[1]->v); 
  }
}pool[N];
inline void rot(node *x){
  node *p=x->f; bool d=x->dir();
  if (!p->isr()) p->f->setc(x,p->dir()); else x->f=p->f;
  p->setc(x->ch[d^1],d); x->setc(p,d^1); p->upd();
}
stack<node*> sta;
inline void splay(node *x){
  node *q=x; for (;!q->isr();q=q->f) sta.push(q); sta.push(q);
  while (!sta.empty()) sta.top()->push(),sta.pop();
  while (!x->isr())
    if (x->f->isr()) rot(x);
    else if (x->dir()==x->f->dir()) rot(x->f),rot(x);
    else rot(x),rot(x);
  x->upd();
}
inline void expose(node *x){
  for (node *q=NULL;x;q=x,x=x->f) splay(x),x->ch[1]=q,x->upd();
}
inline void exert(node *x){ expose(x); splay(x); x->rev^=1; }
inline void link(node *x,node *y) { exert(x); x->f=y; }
inline void cut(node *x,node *y) { exert(y); expose(x); splay(x); x->ch[0]=y->f=NULL; x->upd(); }
node *frt(node *x){ expose(x),splay(x); for (;x->push(),x->ch[0];x=x->ch[0]); return x; }
inline int query(node *x,node *y) { exert(y); expose(x); splay(x); return x->v; }

struct edge{
  int u,v,w,idx;
  bool operator < (const edge & B) const{
    return w<B.w;
  }
}ed[N];

set<int> Set;

int n,m;

int al,ar;
int fat[N];
inline int Fat(int u){
  return u==fat[u]?u:fat[u]=Fat(fat[u]);
}
inline bool Merge(int x,int y){
  x=Fat(x); y=Fat(y); if (x==y) return 0;
  fat[x]=y; return 1;
}
int lst[N],pnt;

int Ans=1<<30;

int main(){
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n); read(m);
  for (int i=1;i<=m;i++)
    read(ed[i].u),read(ed[i].v),read(ed[i].w),ed[i].idx=i;
  sort(ed+1,ed+m+1);
  for (int i=1;i<=m;i++)
    u[i]=ed[i].u,v[i]=ed[i].v,w[i]=ed[i].w,pool[i].idx=pool[i].v=i;
  for (int i=m+1;i<=m+n;i++)
    pool[i].idx=pool[i].v=i,w[i]=1<<30;
  for (int i=1;i<=m;i++){
    int u=::u[i],v=::v[i];
    if (frt(pool+m+u)==frt(pool+m+v)){
      int t=query(pool+m+u,pool+m+v);
      if (w[t]<w[i]){
    cut(pool+m+::u[t],pool+t),cut(pool+m+::v[t],pool+t);
    Set.erase(t);
    link(pool+m+u,pool+i),link(pool+m+v,pool+i);
    Set.insert(i);
      }
    }else
      link(pool+m+u,pool+i),link(pool+m+v,pool+i),Set.insert(i);
    if (Set.size()==n-1){
      int l=*Set.begin(),r=*Set.rbegin();
      if (w[r]-w[l]<Ans)
    Ans=w[r]-w[l],al=l,ar=r;
    }
  }
  for (int i=1;i<=n;i++) fat[i]=i;
  for (int i=al;i<=ar;i++)
    if (Merge(ed[i].u,ed[i].v))
      lst[++pnt]=ed[i].idx;
  sort(lst+1,lst+pnt+1);
  for (int i=1;i<=pnt;i++)
    printf("%d ",lst[i]);
  printf("\n");
  return 0;
}

还有浅显易懂的二分答案 然后 cdq分治加并查集维护连通性
三个log 略慢

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
#define pb push_back
using namespace std;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=100005;

int fat[N],rnk[N];
int Tot;
int pnt,sx[N],sy[N],rx[N];
inline void init(int n){
  for (int i=1;i<=n;i++) fat[i]=i,rnk[i]=0; pnt=0; Tot=n;
}
inline int Fat(int u){
  return u==fat[u]?u:Fat(fat[u]);
}
inline bool Merge(int x,int y){
  x=Fat(x),y=Fat(y); if (x==y) return 0;
  if (rnk[x]<rnk[y]) swap(x,y);
  sx[++pnt]=x; sy[pnt]=y; rx[pnt]=rnk[x]; Tot--;
  fat[y]=x; if (rnk[x]==rnk[y]) rnk[x]++; return 1;
}
inline void Back(int bot){
  while (pnt>bot){
    fat[sy[pnt]]=sy[pnt];
    rnk[sx[pnt]]=rx[pnt];
    pnt--; Tot++;
  }
}

struct edge{
  int u,v,w,idx;
  void read(int i) {::read(u); ::read(v); ::read(w); idx=i; }
  bool operator < (const edge &B) const{
    return w<B.w;
  }
}ed[N];
int u[N],v[N],w[N];

struct info{
  int i,l,r;
  info(int i,int l,int r):i(i),l(l),r(r) { }
};

int End;

inline bool Solve(int l,int r,vector<info> &v0){
  int bot=pnt,mid=(l+r)>>1;
  vector<info> v1,v2;
  for (int i=0;i<(int)v0.size();i++)
    if (v0[i].l<=l && r<=v0[i].r)
      Merge(u[v0[i].i],v[v0[i].i]);
    else{
      if (v0[i].l<=mid) v1.pb(v0[i]);
      if (v0[i].r>mid) v2.pb(v0[i]);
    }
  if (l==r) {
    if (Tot==1) End=l;
    int tmp=Tot;
    Back(bot);
    return tmp==1;
  }
  int ret=Solve(l,mid,v1) || Solve(mid+1,r,v2);
  Back(bot);
  return ret;
}

int n,m;

inline bool check(int x){
  init(n);
  vector<info> v0;
  for (int i=1;i<=m;i++)
    v0.pb(info(i,i,upper_bound(w+1,w+m+1,w[i]+x)-w-1));
  return Solve(1,m,v0);
}

int lst[N],Pnt;

int main(){
  freopen("t.in","r",stdin);
  freopen("t1.out","w",stdout);
  read(n); read(m);
  for (int i=1;i<=m;i++) ed[i].read(i);
  sort(ed+1,ed+m+1);
  for (int i=1;i<=m;i++) u[i]=ed[i].u,v[i]=ed[i].v,w[i]=ed[i].w;
  int L=-1,R=w[m]-w[1]+1,MID;
  while (L+1<R)
    if (check(MID=(L+R)>>1))
      R=MID;
    else
      L=MID;
  int al,ar;
  ar=End; al=lower_bound(w+1,w+m+1,w[ar]-R)-w;
  init(n);
  for (int i=al;i<=ar;i++)
    if (Merge(ed[i].u,ed[i].v))
      lst[++Pnt]=ed[i].idx;
  sort(lst+1,lst+Pnt+1);
  for (int i=1;i<=Pnt;i++)
    printf("%d ",lst[i]);
  printf("\n");
  return 0; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值