【BJOI2014】大融合

本文介绍了一种用于解决动态树形结构问题的树链剖分算法,通过实例讲解了如何处理边的添加及查询边负载的问题。文章详细阐述了算法实现过程,包括离线处理、树链剖分、区间修改等关键技术点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem

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之间有一条边。
Output
对每个查询操作,输出被查询的边的负载。
Sample Input
8
A 2 3
A 3 4
A 3 8
A 8 7
A 6 5
Q 3 8
Sample Output
6
Data Constraint
对于100%的数据,1≤N,Q≤100000

Solution

先离线将所有边连一遍,发现原图是一棵树或是一片森林。
我们将所有森林连成一棵树(原来是一棵树的不用),然后进行树链剖分操作。
假设现在我们要加一条边,那么:
①设该边连着的两个点为x,y(deep[x]<deep[y])
②将以y点为根节点的子树(暂时)的大小加到x点到x点能去到的最上面的祖先(暂时)(这个要用树链剖分修改)
For example:
这里写图片描述
图中的A点就是暂时x点连着的最上面的祖先,假设以y点为根节点的子树大小为delta,那么x到A这一条链就要被区间修改加上delta。
③查询的时候单点查询,所以只有线段树维护的最底层的数据才有效,代表线段树的区间(区间长度2)的数据是不用管它的。

Special


今天一位学长告诉我,C++递归会爆栈的其中一个原因就是C++中每递归一层,在递归内定义的变量又要被重新开一次,导致三万多层递归就爆了。
解决方案有2个:
1.将变量定义到程序开头。
2.在递归中输入static xxxx,这样程序虽然会跑慢一点,但是空间只会被开一次。

这道题目还可以用link-cut-tree来解决,可惜我splay初学。
所以链接待更新。

Code

其实这个代码的树链剖分部分写得并不是很好,还是有许多可以修改的地方。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define eg(i,x) for(i=head[x];i;i=edge[i].next)
#define N 100203
#define LL long long
using namespace std;
struct note{
    LL from,to,next;
};note edge[N*2];
LL n,q,m,k,u,v,x,y,i,j,la;
LL l1,r1;
LL ans,tot,t1,t2;
LL gx,gy,tt;
LL head[N],qu[N][3],siz[N],hv[N];
LL f[N],g[N],fa[N],cn[N][2];
LL deep[N],target[N],order[N],top[N];
LL sum[N*20],lazy[N*20];
bool bz[N];
char ch;
void lb(LL x,LL y)
{
    edge[++tot].from=x;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
LL get(LL n)
{
    if (f[n]==n) return n;
    else
    {
        f[n]=get(f[n]);
        return f[n];
    }
}
void dfs(LL x)
{
    int i;
    bz[x]=1;
    eg(i,x)
        if (edge[i].to!=fa[x])
        {
            fa[edge[i].to]=x;
            dfs(edge[i].to);
        }
}
void dfs1(LL x)
{
    int i;
    bz[x]=1;
    eg(i,x)
        if (edge[i].to!=fa[x])
        {
            deep[edge[i].to]=deep[x]+1;
            dfs1(edge[i].to);
            fa[edge[i].to]=x;
            siz[x]+=siz[edge[i].to];
        }
}
void dfs2(LL x,LL tp)
{
    top[target[order[x]=++target[0]]=x]=tp;
    hv[x]=0;
    eg(i,x)
        if (edge[i].to!=fa[x] && siz[edge[i].to]>siz[hv[x]]) hv[x]=edge[i].to;
    if (hv[x]) dfs2(hv[x],tp);
    eg(i,x)
        if (edge[i].to!=fa[x] && edge[i].to!=hv[x]) dfs2(edge[i].to,edge[i].to);
}
void build(LL s,LL l,LL r)
{
    if (l==r)
    {
        sum[s]=1;
        return;
    }
    LL wz=(l+r)/2;
    build(s*2,l,wz);
    build(s*2+1,wz+1,r);
    sum[s]=sum[s*2]+sum[s*2+1];
}
void change(LL s,LL l,LL r,LL l1,LL r1,LL delta)
{
    if (l==l1 && r==r1)
    {
        sum[s]+=delta;
        lazy[s]+=delta;
        return;
    }
    if (lazy[s])
    {
        sum[s*2]+=lazy[s]; 
        lazy[s*2]+=lazy[s];
        sum[s*2+1]+=lazy[s];
        lazy[s*2+1]+=lazy[s];
        lazy[s]=0;
    }
    LL wz=(l+r)/2;
    if (r1<=wz) change(s*2,l,wz,l1,r1,delta);
        else if (l1>wz) change(s*2+1,wz+1,r,l1,r1,delta);
        else
        {
            change(s*2,l,wz,l1,wz,delta);
            change(s*2+1,wz+1,r,wz+1,r1,delta);
        }
    sum[s]=sum[s*2]+sum[s*2+1];
}
LL find(LL s,LL l,LL r,LL x)
{
    if (l==r) return sum[s];
    if (lazy[s])
    {
        sum[s*2]+=lazy[s]; 
        lazy[s*2]+=lazy[s];
        sum[s*2+1]+=lazy[s];
        lazy[s*2+1]+=lazy[s];
        lazy[s]=0;
    }
    LL wz=(l+r)/2;
    if (x<=wz) return find(s*2,l,wz,x);
          else return find(s*2+1,wz+1,r,x);
    sum[s]=sum[s*2]+sum[s*2+1];
}
void area_ch(LL x,LL y)
{
    LL x1,y1;
    while (1)
    {
        x1=top[x];
        y1=top[y];
        if (deep[x1]<deep[y1])
        {
            swap(x,y);
            swap(x1,y1);
        }
        if (x1==y1) break;
        change(1,1,n,order[x1],order[x],g[gy]);
        x=fa[x1];
    }
    if (order[x]>order[y]) swap(x,y);
    change(1,1,n,order[x],order[y],g[gy]); 
}
int main()
{
    scanf("%lld%lld\n",&n,&q);
    fo(i,1,n) f[i]=i,g[i]=siz[i]=1;
    fo(i,1,q)
    {
        scanf("%c%lld%lld\n",&ch,&u,&v);
        qu[i][0]=u;qu[i][1]=v;
        if (ch=='A')
        {
            lb(u,v);lb(v,u);qu[i][2]=0;
        } else qu[i][2]=1;
    }
    fo(i,1,n)
        if (!bz[i])
        {
            dfs(i);
            if (la) cn[++tt][0]=la;cn[tt][1]=i;
            la=i;
        }
    fo(i,1,tt) lb(cn[i][0],cn[i][1]),lb(cn[i][1],cn[i][0]);
    dfs1(1);
    dfs2(1,1);
    build(1,1,n);
    fo(i,1,q)
    {
        if (!qu[i][2])
        {
            gx=get(qu[i][0]);
            gy=get(qu[i][1]);
            if (gx!=gy)
            {
                if (deep[gx]>deep[gy])
                {
                    swap(gx,gy);
                    swap(qu[i][0],qu[i][1]);
                }
                area_ch(gx,qu[i][0]);
                g[gx]+=g[gy];g[gy]=0;
                f[gy]=gx;
            }
        } else
        {
            if (deep[qu[i][0]]>deep[qu[i][1]]) swap(qu[i][0],qu[i][1]);
            gx=get(qu[i][0]);
            t1=find(1,1,n,order[gx]);
            t2=find(1,1,n,order[qu[i][1]]);
            t1-=t2;
            ans=t1*t2;
            printf("%lld\n",ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值