[LCT 树状数组] HDU 5333 Undirected Graph

这个题跟 [LCT 主席树] BZOJ 3514 Codechef MARCH14 GERALD07加强版有异曲同工之妙
考虑询问按右端点排序
对于一条边 边的权值定义为较小的一端的编号
那么显然权值大的边更有利
对于每个右端点 我们加入所有新增的边 维护最大生成树
询问么 就是n-生成树中编号大于等于左端点 因为这样的边会把两个联通块连在一起

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

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

const int N=100005;

namespace BIT{
  int c[N],maxn;
  inline void init(int n){ maxn=n; for (int i=1;i<=n;i++) c[i]=0; }
  inline void add(int x,int r){ for (int i=x;i<=maxn;i+=i&-i) c[i]+=r; }
  inline int sum(int x){ int ret=0; for (int i=x;i;i-=i&-i) ret+=c[i]; return ret; }
  inline int sum(int l,int r){ return sum(r)-sum(l-1); }
}

int us[N*2],vs[N*2],ws[N*3];
inline int Min(int a,int b){
  return ws[a]<ws[b]?a:b;
}

struct node{
  node *f,*ch[2];
  int idx,v; 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*3];
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();
}
void expose(node *x){
  for (node *q=NULL;x;q=x,x=x->f) splay(x),x->ch[1]=q,x->upd();
}
void exert(node *x){ expose(x); splay(x); x->rev^=1; }
void link(node *x,node *y) { exert(y); y->f=x; }
void cut(node *x,node *y) { exert(x); expose(y); splay(y); y->ch[0]=x->f=NULL; y->upd(); }
node *frt(node *x){ expose(x),splay(x); for (;x->push(),x->ch[0];x=x->ch[0]); return x; }
int query(node *x,node *y){ exert(x); expose(y); splay(y); return y->v; }

int n,m;

struct abcd{
  int l,r,idx;
  bool operator < (const abcd &B) const{ return r<B.r; }
}eve[N*2];
int tot,ans[N];

inline void addedge(int i){
  if (frt(pool+m+us[i])==frt(pool+m+vs[i])){
    int p=query(pool+m+us[i],pool+m+vs[i]);
    if (ws[p]<ws[i]){
      cut(pool+p,pool+m+us[p]),cut(pool+p,pool+m+vs[p]); BIT::add(ws[p],-1);
      link(pool+i,pool+m+us[i]),link(pool+i,pool+m+vs[i]); BIT::add(ws[i],1);
    }
  }else
    link(pool+i,pool+m+us[i]),link(pool+i,pool+m+vs[i]),BIT::add(ws[i],1);
}

inline int query(int i){
  return n-BIT::sum(eve[i].l,eve[i].r);
}

int main(){
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  while (read(n)&&read(m)&&read(tot)){
    for (int i=1;i<=m;i++) { read(eve[i].l),read(eve[i].r); if (eve[i].l>eve[i].r) swap(eve[i].l,eve[i].r); }
    sort(eve+1,eve+m+1);
    for (int i=1;i<=m;i++) us[i]=ws[i]=eve[i].l,vs[i]=eve[i].r;
    for (int i=m+1;i<=m+n;i++) ws[i]=1<<30;
    for (int i=1;i<=m+n;i++) pool[i].idx=pool[i].v=i,pool[i].rev=0,pool[i].f=pool[i].ch[0]=pool[i].ch[1]=NULL;
    for (int i=1;i<=tot;i++) read(eve[i].l),read(eve[i].r),eve[i].idx=i;
    sort(eve+1,eve+tot+1); int pnt1=1,pnt2=1;
    BIT::init(n);
    for (int i=1;i<=n;i++){
      while (pnt1<=m && vs[pnt1]==i)
    addedge(pnt1),pnt1++;
      while (pnt2<=tot && eve[pnt2].r==i)
    ans[eve[pnt2].idx]=query(pnt2),pnt2++;
    }
    for (int i=1;i<=tot;i++) printf("%d\n",ans[i]);
  }
  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值