[bzoj4530][动态树][树链剖分][并查集]大融合

11 篇文章 0 订阅

4530: [Bjoi2014]大融合

Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 715 Solved: 416
[Submit][Status][Discuss]
Description

小强要在N个孤立的星球上建立起一套通信系统。这套通信系统就是连接N个点的一个树。
这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够
联通的树上路过它的简单路径的数量。

例如,在上图中,现在一共有了5条边。其中,(3,8)这条边的负载是6,因
为有六条简单路径2-3-8,2-3-8-7,3-8,3-8-7,4-3-8,4-3-8-7路过了(3,8)。
现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的
询问。
Input

第一行包含两个整数N,Q,表示星球的数量和操作的数量。星球从1开始编号。
接下来的Q行,每行是如下两种格式之一:
A x y 表示在x和y之间连一条边。保证之前x和y是不联通的。
Q x y 表示询问(x,y)这条边上的负载。保证x和y之间有一条边。
1≤N,Q≤100000
Output

对每个查询操作,输出被查询的边的负载。
Sample Input

8 6

A 2 3

A 3 4

A 3 8

A 8 7

A 6 5

Q 3 8
Sample Output

6

HINT

Source

鸣谢佚名上传

做法1:

显然可以用lct维护子树信息过。
有兴趣的可以
http://blog.csdn.net/neither_nor/article/details/52979425
其实就是比普通的lct多维护了一个虚子树的信息,因为虚子树的信息只会在link和access的时候改变,所以处理一下即可。因为暂时没有出现要支持链信息修改的题,这里只有口胡一下。
比如我要修改x到y这条链的信息,实际上因为有虚边的话我们要维护虚边对应的splay上的虚子树信息,这没法做,所以这类问题都可以通过make_root来使得虚边消失掉,那么实子树都很好维护了。所以这个做法应该完全可以拓展到维护子树的权值和。并且支持链信息修改?如果我们的标记add[x]表示增加了add[x]*size[x]的话,把这个标记往实子树下面传的话应该也是不会做的。

做法2:

事先把树建好,然后并查集,加树剖
因为不会做法1,所以这里只讲做法2。
我们考虑所有的边都已经给出来了,那么我们可以事先把树建好,显然对于一个连边,就是连上了一对父子,我们用并查集维护一个联通块中深度最小的点,那么并查集很好维护,对于一个询问x,y,假设x深度较大,其实答案就是size[x]*(并查集大小-size[x])。并查集大小好维护,但是怎么知道size[x]呢?
我们考虑用一个数组f[i]表示i的父亲和i连接的边被砍掉后i的size,那这个f数组什么时候会改变呢,显然是两个联通块合并起来的时候深度较大的那个根的父亲到深度较小的那个根上的f值都会变,并且是加上了下面那个联通块的大小。

做法1:

#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
typedef long long ll;
int n,m;
inline int read()
{
    char c;
    int res,flag=0;
    while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
    res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
    return flag?-res:res;
}
const int N=110000;
int sum1[N],sum2[N];
int lc[N],rc[N],size[N],fa[N];
bool rev[N];
inline void updata(int x)
{
    sum1[x]=sum1[lc[x]]+sum1[rc[x]]+1+sum2[x];
}
inline bool is_root(int x)
{
    return lc[fa[x]]!=x&&rc[fa[x]]!=x;
}
inline void rotate(int x)
{
    int y=fa[x],z=fa[y];
    int b=lc[y]==x?rc[x]:lc[x];
    if(b) fa[b]=y;
    fa[y]=x;fa[x]=z;
    if(z)
    {
        if(lc[z]==y) lc[z]=x;
        if(rc[z]==y) rc[z]=x;
    }
    if(lc[y]==x) lc[y]=b,rc[x]=y;
    else rc[y]=b,lc[x]=y;
    updata(y);
}
inline void tag_rev(int x)
{
    rev[x]=!rev[x];
    swap(lc[x],rc[x]);
}
inline void tag_down(int x)
{
    if(rev[x])
    {
        if(lc[x]) tag_rev(lc[x]);
        if(rc[x]) tag_rev(rc[x]);
        rev[x]=0;
    }
}
int sta[N];
inline void splay(int x)
{
    sta[sta[0]=1]=x;
    for(int y=x;!is_root(y);y=fa[y]) sta[++sta[0]]=fa[y];
    while(sta[0]) tag_down(sta[sta[0]--]);
    while(!is_root(x))
    {
        if(!is_root(fa[x]))
        {
            if((lc[fa[x]]==x)==(lc[fa[fa[x]]]==fa[x])) rotate(fa[x]);
            else rotate(x);
        }
        rotate(x);
    }
    updata(x);
}
inline void access(int q)
{
    for(int p=0;q;p=q,q=fa[q])
    {
        splay(q);
        sum2[q]+=sum1[rc[q]];
        rc[q]=p;
        sum2[q]-=sum1[rc[q]];
        updata(q);
    }
}
inline void make_root(int x)
{
    access(x);
    splay(x);
    tag_rev(x);
}
inline void link(int x,int y)
{
    make_root(x);
    access(y);
    splay(y);
    fa[x]=y;
    sum2[y]+=sum1[x];
    updata(y);
}
char sr[5];
int main()
{
//  freopen("4530.in","r",stdin);
//  freopen(".out","w",stdout);
    n=read();
    m=read();
    for(int i=1;i<=m;++i)
    {
        scanf("%s",sr+1);
        int x,y;
        x=read();
        y=read();
        if(sr[1]=='A') link(x,y);
        else
        {
            make_root(x);
            access(y);
            splay(y);
            printf("%lld\n",(ll)sum1[x]*(sum1[y]-sum1[x]));
        } 
    }
}

做法2:

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
using namespace std;
typedef long long ll;
typedef double s64;
int n,m;
inline int read()
{
    char c;
    int res,flag=0;
    while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
    res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
    return flag?-res:res;
}
const int N=210000;
struct cc
{
    int ty,x,y;
}a[N];
int size[N],fa[N],top[N],son[N],dep[N],seg[N];
int father[N];
const int M=410000;
int f[M],fir[N],go[N],nex[N],tot;
inline void add(int x,int y)
{
    nex[++tot]=fir[x];fir[x]=tot;go[tot]=y;
    nex[++tot]=fir[y];fir[y]=tot;go[tot]=x;
}
inline int find(int x)
{
    return x==father[x]?x:father[x]=find(father[x]);
}
inline void dfs1(int u)
{
    int e,v;
    size[u]=1;
    dep[u]=dep[fa[u]]+1;
    for(e=fir[u];v=go[e],e;e=nex[e])
    if(v!=fa[u])
    {
        fa[v]=u;
        dfs1(v);
        size[u]+=size[v];
        if(size[v]>size[son[u]]) son[u]=v;
    }
}
inline void dfs2(int u,int d)
{
    seg[u]=++seg[0];
    top[u]=d;
    if(son[u]) dfs2(son[u],d);
    else return;
    int e,v;
    for(e=fir[u];v=go[e],e;e=nex[e])
    if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
inline void modify(int k,int l,int r,int L,int R,int v)
{
    if(L<=l&&r<=R)
    {
        f[k]+=v;
        return;
    }
    int mid=l+r>>1;
    if(mid>=L) modify(k<<1,l,mid,L,R,v);
    if(mid< R) modify(k<<1|1,mid+1,r,L,R,v);
}
inline int query(int k,int l,int r,int x)
{
    if(l==r) return f[k];
    int mid=l+r>>1;
    if(mid>=x) return f[k]+query(k<<1,l,mid,x);
    else return f[k]+query(k<<1|1,mid+1,r,x);
}
inline void change(int x,int y)
{
    int v=query(1,1,n,seg[x]);
    x=fa[x];
    int fx=top[x],fy=top[y];
    while(fx!=fy)
    {
        if(dep[fx]<dep[fy]) swap(x,y),swap(fx,fy);
        modify(1,1,n,seg[fx],seg[x],v);
        x=fa[fx];fx=top[x];
    }
    modify(1,1,n,seg[y],seg[x],v);
}
inline void unit(int x,int y)
{
    int fx=find(x),fy=find(y);
    if(dep[fx]<dep[fy]) swap(x,y),swap(fx,fy);
    change(fx,fy);
    father[fx]=fy;
}
inline void query(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    int ans=query(1,1,n,seg[x]);
    ans=(query(1,1,n,seg[find(x)])-ans)*ans;
    printf("%d\n",ans);
}
char sr[5];
int main()
{
//  freopen("4530.in","r",stdin);
//  freopen(".out","w",stdout);
    n=read();
    m=read();
    for(int i=1;i<=m;++i)
    {
        scanf("%s",sr+1);
        if(sr[1]=='Q') a[i].ty=1;
        a[i].x=read();
        a[i].y=read();
        if(!a[i].ty)
        add(a[i].x,a[i].y);
    }
    for(int i=1;i<=n;++i) father[i]=i;
    for(int i=1;i<=n;++i)
    if(!size[i])
    {
        dfs1(i);
        dfs2(i,i);
    }
    f[1]=1;
    for(int i=1;i<=m;++i)
    {
        if(a[i].ty) query(a[i].x,a[i].y);
        else unit(a[i].x,a[i].y);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值