[LCT 主席树] BZOJ 3514 Codechef MARCH14 GERALD07加强版

N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数。
先预处理
按时间做一通最大生成树
每次加边 i 替换树上的最小边 记这条边为 a[i]
特殊的 如果本来不连通 a[i]=0 如果是自环 a[i]=i

对于每个询问 [l,r] ,统计区间 [l,r] 中有多少个数小于 l ,用n去减即可得答案
为什么呢 因为替换了一条小于 l 的边 那么相当于只保留[l,r]的边时 连接了两个连通块

今天换了一种姿势码LCT 代码短短哒 总行数只有100

#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 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=400005;

int n,m;
int us[N],vs[N],ws[N];
inline int Min(int x,int y) { return ws[x]<ws[y]?x:y; }

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],*cur;
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 root[N],ls[N*30],rs[N*30],s[N*30];
int ncnt;

inline void add(int &x,int y,int l,int r,int t){
  x=++ncnt; int mid=(l+r)>>1;
  s[x]=s[y]+1; if (l==r) return;
  if (t<=mid)
    add(ls[x],ls[y],l,mid,t),rs[x]=rs[y];
  else
    add(rs[x],rs[y],mid+1,r,t),ls[x]=ls[y];
}
inline int query(int x,int l,int r,int ql,int qr){
  if (ql<=l && r<=qr) return s[x];
  int ret=0,mid=(l+r)>>1;
  if (ql<=mid) ret+=query(ls[x],l,mid,ql,qr);
  if (qr>mid) ret+=query(rs[x],mid+1,r,ql,qr);
  return ret;
}

int a[N];

int main(){
  int Q,lastans=0,t,l,r;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n); read(m); read(Q); read(t);
  for (int i=1;i<=m;i++) read(us[i]),read(vs[i]),ws[i]=i;
  for (int i=m+1;i<=m+n;i++) ws[i]=1<<30;
  for (int i=1;i<=n+m;i++) pool[i].idx=pool[i].v=i;
  for (int i=1;i<=m;i++){
    if (us[i]==vs[i]) { a[i]=i; continue; }
    if (frt(pool+m+us[i])==frt(pool+m+vs[i])){
      int p=query(pool+m+us[i],pool+m+vs[i]); a[i]=p;
      cut(pool+p,pool+m+us[p]),cut(pool+p,pool+m+vs[p]);
      link(pool+i,pool+m+us[i]),link(pool+i,pool+m+vs[i]);
    }else
      link(pool+i,pool+m+us[i]),link(pool+i,pool+m+vs[i]),a[i]=0;
  }
  for (int i=1;i<=m;i++) add(root[i],root[i-1],0,m,a[i]);
  while (Q--){
    read(l); read(r); l^=lastans*t,r^=lastans*t;
    int tem=query(root[r],0,m,0,l-1)-query(root[l-1],0,m,0,l-1);
    printf("%d\n",lastans=n-tem);
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值