码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj 3514 Codechef MARCH14 GERALD07加强版

时间:2019-10-13 20:49:19      阅读:74      评论:0      收藏:0      [点我收藏+]

标签:include   com   get   end   并且   split   char   spl   space   

bzoj

题目要的连通块个数可以表示为点数\(-\)所有生成树上的边数.考虑这个生成树边数,我们维护编号最大生成树,按照编号加入边,然后如果加的时候会成环就把环上编号最小的边挤掉,并且当前的第\(i\)条边的前驱边\(pre_i\)为刚才被挤掉的第\(j\)条边,如果没有前驱边就是0

然后对于一个询问,我们只把使得联连通块个数减少的边放在生成树上,也就是如果区间\([l,r]\)中同时存在\(i\)\(pre_i\),就不统计\(i\)的贡献,那么有贡献的边都会满足\(pre_i<l\le i\le r\),这个可以建出主席树后二维数点实现

#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=2e5+10,inf=1<<30;
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
int n,m,q,op,ans,e[N<<1][2];
int fa[N<<1],ch[N<<1][2],id[N<<1];
bool tg[N<<1];
bool nrt(int x){return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}
void psup(int x){id[x]=min(x<=n?inf:x,min(id[ch[x][0]],id[ch[x][1]]));}
void rev(int x){if(x) tg[x]^=1,swap(ch[x][0],ch[x][1]);}
void psdn(int x){if(tg[x]) rev(ch[x][0]),rev(ch[x][1]),tg[x]=0;}
void ppush(int x)
{
    if(nrt(x)) ppush(fa[x]);
    psdn(x);
}
void rot(int x)
{
    int y=fa[x],z=fa[y],ty=ch[y][1]==x,w=ch[x][!ty];
    if(nrt(y)) ch[z][ch[z][1]==y]=x;
    ch[x][!ty]=y,ch[y][ty]=w;
    if(w) fa[w]=y;
    fa[y]=x,fa[x]=z;
    psup(y);
}
void spl(int x)
{
    ppush(x);
    while(nrt(x))
    {
        int y=fa[x],z=fa[y];
        if(nrt(y)) ((ch[y][1]==x)^(ch[z][1]==y))?rot(x):rot(y);
        rot(x);
    }
    psup(x);
}
void acs(int x)
{
    for(int y=0;x;y=x,x=fa[x])
        spl(x),ch[x][1]=y,psup(x);
}
void mkrt(int x){acs(x),spl(x),rev(x);}
void split(int x,int y){mkrt(x),acs(y),spl(y);}
int fdrt(int x)
{
    acs(x),spl(x);
    psdn(x);
    while(ch[x][0]) x=ch[x][0],psdn(x);
    return x;
}
void link(int i)
{
    int x=e[i][0],y=e[i][1];
    mkrt(x),mkrt(y);
    fa[x]=fa[y]=i;
}
void cut(int i)
{
    int x=e[i][0],y=e[i][1];
    split(x,y);
    spl(i);
    ch[i][0]=ch[i][1]=fa[x]=fa[y]=0;
}
vector<int> pt[N];
vector<int>::iterator it;
int s[N*30],sn[N*30][2],rt[N],tt;
void inst(int o1,int o2,int x)
{
    s[o1]=s[o2]+1;
    int l=1,r=m;
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(x<=mid)
        {
            sn[o1][0]=++tt,sn[o1][1]=sn[o2][1];
            o1=sn[o1][0],o2=sn[o2][0];
            r=mid;
        }
        else
        {
            sn[o1][0]=sn[o2][0],sn[o1][1]=++tt;
            o1=sn[o1][1],o2=sn[o2][1];
            l=mid+1;
        }
        s[o1]=s[o2]+1;
    }
}
int quer(int o,int l,int r,int ll,int rr)
{
    if(!s[o]||ll>rr) return 0;
    if(ll<=l&&r<=rr) return s[o];
    int an=0,mid=(l+r)>>1;
    if(ll<=mid) an+=quer(sn[o][0],l,mid,ll,rr);
    if(rr>mid) an+=quer(sn[o][1],mid+1,r,ll,rr);
    return an;
}

int main()
{
    n=rd(),m=rd(),q=rd(),op=rd();
    id[0]=inf;
    for(int i=1;i<=m;++i)
    {
        e[i+n][0]=rd(),e[i+n][1]=rd();
        if(e[i+n][0]==e[i+n][1]) continue;
        if(fdrt(e[i+n][0])==fdrt(e[i+n][1]))
        {
            split(e[i+n][0],e[i+n][1]);
            int ii=id[e[i+n][1]];
            pt[ii-n].push_back(i);
            cut(ii);
        }
        else pt[0].push_back(i);
        link(i+n);
    }
    for(int i=1;i<=m;++i)
    {
        rt[i]=rt[i-1];
        for(it=pt[i-1].begin();it!=pt[i-1].end();++it)
        {
            int x=*it,las=rt[i];
            inst(rt[i]=++tt,las,x);
        }
    }
    while(q--)
    {
        int l=rd()^(ans*op),r=rd()^(ans*op);
        ans=n-quer(rt[l],1,m,l,r);
        printf("%d\n",ans);
    }
    return 0;
}

bzoj 3514 Codechef MARCH14 GERALD07加强版

标签:include   com   get   end   并且   split   char   spl   space   

原文地址:https://www.cnblogs.com/smyjr/p/11668046.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!