Codechef MARCH14 GERALD07加强版

强制在线不代表不能预处理!

考虑暴力怎么干?

开始n个联通块。now=n

不断加入边,如果连接两个联通块,--now

否则不动。

后者的前提是和[l,id-1]的边构成环

 

所以,我们考虑每个[l,r]中出现的边能否第一次连接两个联通块

预处理:

编号从小到大加入每条边,LCT维护树上“边”编号最小值和最小值位置

如果加入边e没有环,那么说明无论什么时候[l,r]询问包含e的时候,e总能是第一个连接两个联通块的边,设ti[e]=0,表示不能替换边

如果会成环,那么把编号最小的边删掉,ti[i]=被删边的编号。意义是,只有[l,r]不包含这条被删除的边的时候,e才会贡献。

    如果包含,那么这个环一定会连出来;如果不包含,e和这个环上其他的边不会构成环,加入e的时候有贡献。(如果会构成环,那么环上边和被删边之前也能构成环,矛盾)

所以,ti[i]在不在[l,r]之中,唯一对应了i能否真正连接两个联通块!

所以对于询问,主席树维护即可。[l,r]中,ti[i]<l的数的个数sum,n-sum就是ans

 

实现细节:
LCT的编号别和原边的编号混了。。。。

mi是最小边编号(最小值),id是最小值的lct点的编号!

调半天~~~

吸取重组病毒的教训,把主席树封装进namespace~~

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=200000+5;
const int inf=0x3f3f3f3f;
struct bian{
    int x,y;
}e[N];
int n,m,k,typ;
int ti[N];
namespace seg{
#define mid ((l+r)>>1)
struct node{
    int ls,rs;
    int val;
}t[N*22];
int cnt;
int rt[N];
void upda(int &x,int y,int l,int r,int to){
    x=++cnt;
    t[x].val=t[y].val+1;
    if(l==r) return;
    t[x].ls=t[y].ls,t[x].rs=t[y].rs;
    if(to<=mid) upda(t[x].ls,t[y].ls,l,mid,to);
    if(mid<to) upda(t[x].rs,t[y].rs,mid+1,r,to);
}
int query(int x,int y,int l,int r,int L,int R){
    //cout<<" l r "<<l<<" "<<r<<" : "<<t[x].val<<" "<<t[y].val<<" "<<L<<" "<<R<<endl;
    if(L<=l&&r<=R){
        //cout<<" l r "<<l<<" "<<r<<" : "<<t[x].val<<" "<<t[y].val<<" "<<L<<" "<<R<<endl;
        return t[x].val-t[y].val;
    }
    int ret=0;
    if(L<=mid) ret+=query(t[x].ls,t[y].ls,l,mid,L,R);
    if(mid<R) ret+=query(t[x].rs,t[y].rs,mid+1,r,L,R);
    return ret;
}
void add(int x,int to){
    upda(rt[x],rt[x-1],0,m,to);
}
}
namespace lct{
#define ls t[x].ch[0]
#define rs t[x].ch[1]
struct node{
    int ch[2];
    int fa,r;
    int id;
    int mi;
    int d;
}t[2*N];
int sta[N];
int cnt;
bool nrt(int x){
    return (t[t[x].fa].ch[0]==x||t[t[x].fa].ch[1]==x);
}
void pushup(int x){
    if(t[t[x].ch[0]].mi<=t[x].d&&t[t[x].ch[0]].mi<=t[t[x].ch[1]].mi){
        t[x].mi=t[t[x].ch[0]].mi;
        t[x].id=t[t[x].ch[0]].id;
    }
    else if(t[t[x].ch[1]].mi<=t[x].d&&t[t[x].ch[1]].mi<=t[t[x].ch[0]].mi){
        t[x].mi=t[t[x].ch[1]].mi;
        t[x].id=t[t[x].ch[1]].id;
    }
    else t[x].mi=t[x].d,t[x].id=x;
}
void rev(int x){
    swap(t[x].ch[0],t[x].ch[1]);
    t[x].r^=1;
}
void pushdown(int x){
    if(t[x].r){
        rev(t[x].ch[1]),rev(t[x].ch[0]);
        t[x].r=0;
    }
}
void rotate(int x){
    int y=t[x].fa,d=t[y].ch[1]==x;
    t[t[y].ch[d]=t[x].ch[!d]].fa=y;
    if(nrt(y)) t[t[x].fa=t[y].fa].ch[t[t[y].fa].ch[1]==y]=x;
    else t[x].fa=t[y].fa;
    t[t[x].ch[!d]=y].fa=x;
    pushup(y);
}
void splay(int x){
    int y=x,z=0;
    sta[++z]=y;
    while(nrt(y)) y=t[y].fa,sta[++z]=y;
    while(z) pushdown(sta[z--]);
    
    while(nrt(x)){
        y=t[x].fa,z=t[y].fa;
     //   cout<<"splaying "<<x<<" "<<y<<" "<<z<<endl; 
        if(nrt(y)){
            rotate(((t[z].ch[1]==y)==(t[y].ch[1]==x))?y:x);
        }
        rotate(x);
    }
    pushup(x);
}
void access(int x){
    for(reg y=0;x;y=x,x=t[x].fa){
        
        splay(x);t[x].ch[1]=y;pushup(x);
    //    cout<<" access xx "<<x<<" "<<t[x].fa<<endl;
    }
}
void makert(int x){
    access(x);splay(x);rev(x);
}
int findrt(int x){
    access(x);splay(x);
//    cout<<" after splay "<<x<<endl;
    pushdown(x);
    while(t[x].ch[0]) {
        x=t[x].ch[0],pushdown(x);
//        cout<<"findrt xx "<<x<<endl;
    }
    splay(x);
    return x;
}
int link(int x,int y,int d){
//    cout<<" link "<<x<<" "<<y<<" : "<<d<<" cnt "<<cnt<<endl;
    makert(x);
//    cout<<" makert "<<endl;
    ++cnt;
    t[cnt].d=d;
    t[cnt].id=cnt;
    t[cnt].mi=d;
    if(findrt(y)!=x){
    //    cout<<" new "<<endl;
        access(y);splay(y);
        t[x].fa=cnt;
        t[cnt].fa=y;
        return 0;
    }    
    pushup(x);
    splay(x);
    int kil=t[x].id;
    int lp=t[x].mi;
//    cout<<" ---------------------kil "<<kil<<endl;
    splay(kil);
//    cout<<" kilfa "<<t[kil].fa<<endl;
    t[t[kil].ch[0]].fa=0;
    t[t[kil].ch[1]].fa=0;
    makert(x);
    splay(x);splay(y);
    t[x].fa=cnt;
    t[cnt].fa=y;
    //cout<<" t[x].id "<<t[x].id<<" t[y].id "<<t[y].id<<endl;
    return lp;
}
}
int main(){
    lct::t[0].id=-1;
    lct::t[0].d=inf;
    lct::t[0].mi=inf;
    rd(n);rd(m);rd(k);rd(typ);
    for(reg i=1;i<=n;++i) lct::t[i].d=inf,lct::t[i].id=-1,lct::t[i].mi=inf;
    lct::cnt=n;
    int x,y;
    for(reg i=1;i<=m;++i){
        rd(x);rd(y);
        if(x!=y) ti[i]=lct::link(x,y,i);
        else ti[i]=i;
        //cout<<" ii "<<i<<" "<<ti[i]<<endl;
    }
    for(reg i=1;i<=m;++i){
        seg::add(i,ti[i]);
    }
    int l,r;
    int las=0;
    while(k--){
        rd(l);rd(r);
        if(typ==1) l^=las,r^=las;
        int tmp=seg::query(seg::rt[r],seg::rt[l-1],0,m,0,l-1);
        printf("%d\n",n-tmp);
        las=n-tmp;
    }
    return 0;
}

}
signed main(){
//freopen("data.in","r",stdin);
//    freopen("my.out","w",stdout);
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2018/12/30 21:24:18
*/


    

 思路和区间数颜色挺像的!都是把贡献放在第一个能贡献的位置统计上!

并且方法也是类似的:离线:排序扫描线+树状数组,在线:主席树

posted @ 2018-12-31 09:33  *Miracle*  阅读(160)  评论(0编辑  收藏  举报