【BZOJ 1130】【POI 2007】大都市meg【树链剖分】

Description

  昔日,乡下有依次编号为1..n的n个小村庄,某些村庄之间有一些双向的土路。从每个村庄都恰好有一条路径到达村庄1(即比特堡)。并且,对于每个村庄,它到比特堡的路径恰好只经过编号比它的编号小的村庄。另外,对于所有道路而言,它们都不在除村庄以外的其他地点相遇。现在,越来越多的土路被改造成了公路。 Blue Mary想起了在改造期间她送信的经历。她从比特堡出发,需要去某个村庄,并且在两次送信经历的间隔期间,有某些土路被改造成了公路。现在Blue Mary需要你的帮助:计算出每次送信她需要走过的土路数目。

Input

  第一行是一个数n(1 < = n < = 2 50000)。以下n-1行,每行两个整数a,b。以下一行包含一个整数m(1 < = m < = 2 50000),表示Blue Mary曾经在改造期间送过m次信。以下n+m-1行,每行有两种格式的若干信息,表示按时间先后发生过的n+m-1次事件:若这行为 A a b(a若这行为 W a, 则表示Blue Mary曾经从比特堡送信到村庄a。

Output

  有m行,每行包含一个整数,表示对应的某次送信时经过的土路数目。

题解

裸树链剖分,没什么好说的。
除了要注意输入是在遇到m个w后停止。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>

#define N 250010
using namespace std;

struct node{int to,next;}e[N*2];
int head[N],m,tim,n;
int size[N],son[N],top[N],fa[N],tid[N],dep[N];

void init()
{
    memset(head,0,sizeof(head));
    memset(son,-1,sizeof(son));
    tim = m = 0;
}

void add_edge(int from,int to)
{
    e[++m].next = head[from];
    head[from] = m;
    e[m].to = to;
}

void dfs1(int v,int pa,int d)
{
    size[v] = 1; fa[v] = pa; dep[v] = d;
    for(int i = head[v];i;i=e[i].next)
        if(e[i].to != pa) {
            dfs1(e[i].to,v,d+1);
            size[v] += size[e[i].to];
            if(son[v] == -1 || size[son[v]] < size[e[i].to])
                son[v] = e[i].to;
        }
}

void dfs2(int v,int tp)
{
    top[v] = tp; tid[v] = ++tim;
    if(son[v] == -1) return;
    dfs2(son[v],tp);
    for(int i = head[v];i;i=e[i].next)
        if(e[i].to != fa[v] && e[i].to != son[v])
            dfs2(e[i].to,e[i].to);
}

#define lson o << 1
#define rson o << 1 | 1
int sum[N << 2];

void build(int o,int l,int r)
{
    if(l == r) {sum[o] = 1; return;}
    int mid = (l+r)>>1;
    build(lson,l,mid); build(rson,mid+1,r);
    sum[o] = sum[lson] + sum[rson];
}

void change(int o,int l,int r,int pos)
{
    if(l == r) {sum[o] = 0; return;}
    int mid = (l+r)>>1;
    if(pos <= mid) change(lson,l,mid,pos); else change(rson,mid+1,r,pos);
    sum[o] = sum[lson] + sum[rson];
}

int query(int o,int l,int r,int ll,int rr)
{
    if(ll <= l && rr >= r) return sum[o];
    int mid = (l+r)>>1,ans = 0;
    if(ll <= mid) ans += query(lson,l,mid,ll,rr);
    if(rr > mid) ans += query(rson,mid+1,r,ll,rr);
    return ans;
}

int Query(int x,int y)
{
    int ans = 0;
    while(top[x] != top[y])
    {
        if(dep[top[x]] < dep[top[y]]) swap(x,y);
        ans += query(1,1,n,tid[top[x]],tid[x]);
        x = fa[top[x]];
    }
    if(dep[x] > dep[y]) swap(x,y);
    ans += query(1,1,n,tid[x]+1,tid[y]);
    return ans;
}

int main()
{
    char opt[3];
    int u,v;
    scanf("%d",&n);
    init();
    for(int i = 1;i < n;i++) {
        scanf("%d%d",&u,&v);
        add_edge(u,v); add_edge(v,u);
    }
    dfs1(1,0,0);
    dfs2(1,1);
    build(1,1,n);

    scanf("%d",&m);
    int z = 0;
    while(z < m) {
        scanf("%s%d",opt,&u);
        if(opt[0] == 'A') {
            scanf("%d",&v);
            if(u > v) swap(u,v);
            change(1,1,n,tid[v]);
        }
        else {z++; printf("%d\n",Query(1,u));}
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值