bzoj4530: [Bjoi2014]大融合 //线段树分治+并查集

bzoj4530: [Bjoi2014]大融合


题意

N<=1e5个点,Q<=1e5个操作。
支持加一条边(u,v)(保证图是森林)、询问经过边(u,v)的简单路径条数(保证(u,v)存在)。


题解

/*本来是想当作lct维护子树信息例题的,然而一眼线段树分治,于是写了一下,以为可以很快码完…。
结果发现代码奇长无比,运行奇慢无比,而且还调了半天QAQ
大概是写的姿势不对吧*/

如果不算询问的那条边,那么答案就是当前u的联通块大小*v的联通块大小。
所以每条边原本的出现时间(加边的时间~结束)会被询问分隔成几段,所有边的总段数依然是Q级别的。
于是我们愉快地线段树分治顺便并查集维护一下siz就好啦。


代码

#include
#define N 100005
#define V 2000006
using namespace std;
typedef long long ll;
struct edge
{int u,v;}e[N];
mapint>mp;
mapint>::iterator it;
int n,q,tmp,lst[N],qnum[N],cct,cnt,tot,
to[V],hd[V],lk[N<<2],f[N],siz[N],t[20],
no[20][N<<1],ff[20][N<<1],d=-1;
bool used[2][20][N];
void ins(int k,int l,int r,int ll,int rr,int v)
{
    if(ll>rr)return;
    if(l==ll&&r==rr)
    {to[++tot]=v,hd[tot]=lk[k],lk[k]=tot;return;}
    int mid=l+r>>1;
    ins(k<<1,l,mid,ll,rr1|1,mid+1,r,ll<=mid?mid+1:ll,rr,v);
}
int getr(int x)
{
    if(f[x]==x)return x;
    if(!used[1][d][x])
    no[d][t[d]]=x,ff[d][t[d]++]=f[x],used[1][d][x]=1;
    return f[x]=getr(f[x]);
}
void dfs(int k,int l,int r)
{
    d++;
    int x,y;
    for(int i=lk[k];i;i=hd[i])
    {
        x=getr(e[to[i]].u),y=getr(e[to[i]].v);
        if(!used[0][d][y])
        no[d][t[d]]=y,ff[d][t[d]++]=-siz[y],used[0][d][y]=1;
        if(!used[1][d][x])
        no[d][t[d]]=x,ff[d][t[d]++]=f[x],used[1][d][x]=1;
        siz[y]+=siz[x],f[x]=y;
    }
    if(l^r)
    (dfs(k<<1,l,x=l+r>>1),dfs(k<<1|1,x+1,r));
    else
    {
        x=getr(e[qnum[l]].u),y=getr(e[qnum[l]].v);
        printf("%lld\n",(ll)siz[x]*
        siz[y]);
    }
    while(t[d]--)
    (ff[d][t[d]]<0)?
    (siz[no[d][t[d]]]=-ff[d][t[d]],
     used[0][d][no[d][t[d]]]=0):
    (f[no[d][t[d]]]=ff[d][t[d]],
     used[1][d][no[d][t[d]]]=0);
    t[d--]=0;
}
char op[N][2];
ll tp;
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%s%d%d",op[i],&e[i].u,&e[i].v);
        if(e[i].u>e[i].v)swap(e[i].u,e[i].v);
        if(op[i][0]=='Q')qnum[++cnt]=i;
    }
    for(int i=1;i<=q;i++)
    {
        tp=(ll)e[i].u*n+e[i].v;
        if(op[i][0]=='A')
        mp.insert(make_pair(tp,i)),lst[i]=cct+1;
        else
        {
            cct++;
            tmp=mp.find(tp)->second;
            ins(1,1,cnt,lst[tmp],cct-1,tmp);
            lst[tmp]=cct+1;
        }
    }
    for(int i=1;i<=q;i++)
    if(lst[i])ins(1,1,cnt,lst[i],cnt,i);
    for(int i=1;i<=n;i++)siz[f[i]=i]=1;
    dfs(1,1,cnt);
}

你可能感兴趣的:(线段树分治,并查集)