BZOJ 4538 [Hnoi2016]网络

线段树上二分+链交+LCA

这题做法大概有2种,我用的是第二种

1.实际上一条链的贡献是这条链在树上的补集,于是树剖线段树维护树,每一个节点开一个堆维护最大贡献。 O(nlog3n)

2.假设对于一个询问x,二分出一个答案C,若所有≥C的链都经过了x,则显然最终答案<C,否则最终答案≥C。于是离线将所有链按权排序后,建立权值线段树,节点存储子节点的链交,直接在线段树上二分找答案即可, O(nlog2n) ,如果把LCA优化成O(1)即可做到 O(nlogn)

顺便补充一下如何求链交,网络上求链交的看不懂,于是只好自己脑补一个。

记链为(LCA,a,b),a,b为两端点,链的深度指LCA的深度,定义max(a,b)为取a,b中深度大的,lca(a,b)为取a,b的LCA。

先判断有没有交,方法是判断深度大链的LCA在不在深度小的链上,若不在则无交集。

否则有交,
若LCA1 = LCA2 ,链交的LCA不变,两端点分别取max即可,就不详细说了。
若LCA1 ≠ LCA2,根据脑补+画图可以知道链交的两个端点分别是
a = max( lca(a1,b2), lca(a2,b1) )
b = max( lca(a1,a2), lca(b1,b2) )
这个式子应该具有一般性,可以直接套用(只是常数比较大)

#include<cstdio>
#include<algorithm>
#define N 200005
#define H 20
#define reg register
using namespace std;
namespace runzhe2000
{
    const int INF = 1<<30;
    int ecnt, last[N], cnt, timer, beg[N], end[N], fa[N], dep[N], list[N*4], que[N], ST[N*4][H], pos[N], log[N*4], bin[H];
    struct edge{int next,to;}e[N<<1];
    struct opt
    {
        int type, a, b, v;
    }q[N];
    struct chain
    {
        int lca, a, b;
        inline bool in(reg int x)
        {
            if(lca == 0)return false;
            else if(lca == -1)return true;
            return beg[lca] <= beg[x] && beg[x] <= end[lca] &&( (beg[x] <= beg[a] && beg[a] <= end[x]) || (beg[x] <= beg[b] && beg[b] <= end[x]) );
        }
    };
    struct seg
    {
        int l, r, v, tot;
        chain c;
    }t[N*5];
    bool cmp(int a, int b){return q[a].v < q[b].v;}
    void addedge(reg int a, reg int b)
    {
        e[++ecnt] = (edge){last[a], b};
        last[a] = ecnt;
    }
    inline int dep_min(reg int x, reg int y){return dep[x] < dep[y] ? x : y;}
    inline int dep_max(reg int x, reg int y){return dep[x] > dep[y] ? x : y;}
    void dfs(int x)
    {
        dep[x] = dep[fa[x]] + 1;
        list[++timer] = x;
        beg[x] = timer;
        for(int i = last[x]; i; i = e[i].next)
        {
            int y = e[i].to;
            if(y == fa[x])continue;
            fa[y] = x;
            dfs(y);
            list[++timer] = x;
        }
        end[x] = timer;
    }
    void build_ST()
    {
        dep[0] = INF;
        bin[0] = 1;for(int i = 1; i < H; i++)bin[i] = bin[i-1] << 1;
        for(reg int i = 2; i <= timer; i++)log[i] = 1 + log[i>>1];
        for(reg int i = 1; i <= timer; i++)ST[i][0] = list[i];
        for(reg int j = 1; j < H; j++)
            for(reg int i = 1; i <= timer; i++)
                ST[i][j] = dep_min(ST[i][j-1], ST[i+bin[j-1]][j-1]);
    }
    int ask_lca(reg int x, reg int y)
    {
        if(x==y)return x;
        reg int a = beg[x], b = beg[y];
        if(a>b)swap(a,b);
        reg int len = b-a+1;
        return dep_min(ST[a][log[len]], ST[b-bin[log[len]]+1][log[len]]);
    }
    void pushup(int x)
    {
        int lson = x<<1, rson = x<<1|1, u, v;
        if(t[lson].c.lca == -1)t[x].c = t[rson].c;
        else if(t[rson].c.lca == -1)t[x].c = t[lson].c;
        else if(t[lson].c.lca == 0 || t[rson].c.lca == 0)t[x].c = (chain){0,0,0};
        else if(!t[lson].c.in(t[rson].c.lca) && !t[rson].c.in(t[lson].c.lca))t[x].c = (chain){0,0,0};
        else if(t[lson].c.lca == t[rson].c.lca)
        {
            u = dep_max(ask_lca(t[lson].c.a, t[rson].c.a), ask_lca(t[lson].c.a, t[rson].c.b));
            v = dep_max(ask_lca(t[lson].c.b, t[rson].c.a), ask_lca(t[lson].c.b, t[rson].c.b));
            t[x].c = (chain){t[lson].c.lca, u, v};
        }
        else
        {
            u = dep_max(ask_lca(t[lson].c.a, t[rson].c.b), ask_lca(t[lson].c.b, t[rson].c.a));
            v = dep_max(ask_lca(t[lson].c.a, t[rson].c.a), ask_lca(t[lson].c.b, t[rson].c.b));
            t[x].c = (chain){ask_lca(u, v), dep_min(u,v), dep_max(u,v)};
        }

        if(t[x].c.lca==-1 || t[x].c.lca==0)t[x].v=-1;
        else t[x].v=max(t[lson].v, t[rson].v);
        t[x].tot = t[x<<1].tot + t[x<<1|1].tot;
    }
    void build(int x, int l, int r)
    {
        t[x].l = l;
        t[x].r = r;
        t[x].c = (chain){t[x].v = -1,0,0};
        t[x].tot = 0;
        if(l==r)return;
        int mid = (l+r)>>1;
        build(x<<1,l,mid);
        build(x<<1|1,mid+1,r);
    }
    void modify(reg int x, reg int p, reg int id)
    {
        if(t[x].l==t[x].r)
        {
            if(!id)t[x].c = (chain){-1,0,0}, t[x].v=-1, t[x].tot = 0;
            else t[x].c = (chain){ask_lca(q[id].a, q[id].b), q[id].a, q[id].b}, t[x].v=q[id].v, t[x].tot = 1;
            return;
        }
        int mid = (t[x].l+t[x].r)>>1;
        if(p<=mid)modify(x<<1,p,id);
        else modify(x<<1|1,p,id);
        pushup(x);
    }
    int query(reg int x, reg int p)
    {
        if(t[x].l == t[x].r)return t[x].c.in(p)?-1:t[x].v;
        else if(!t[x<<1|1].c.in(p))return query(x<<1|1,p);
        else return query(x<<1,p);
    }
    void main()
    {
        int n, m;
        scanf("%d%d",&n,&m);
        for(reg int i = 1, a, b; i < n; i++)
        {
            scanf("%d%d",&a,&b);
            addedge(a,b);
            addedge(b,a);
        }
        dfs(1);
        build_ST();
        for(reg int i = 1; i <= m; i++)
        {
            scanf("%d",&q[i].type);
            if(q[i].type == 0)
            {
                que[++cnt] = i;
                scanf("%d%d%d",&q[i].a,&q[i].b,&q[i].v);
            }
            else if(q[i].type == 1)
                scanf("%d",&q[i].v);
            else
                scanf("%d",&q[i].v);
        }
        sort(que+1,que+cnt+1,cmp);
        for(reg int i = 1; i <= cnt; i++)pos[que[i]] = i;
        if(cnt)build(1,1,cnt);
        for(reg int i = 1; i <= m; i++)
        {
            if(q[i].type == 0)
                modify(1,pos[i],i);
            else if(q[i].type == 1)
                modify(1,pos[q[i].v],0);
            else
            {   
                if(!t[1].tot || t[1].c.in(q[i].v))printf("-1\n");
                else printf("%d\n",query(1,q[i].v));
            }
        }
    }
}
int main()
{
    runzhe2000::main();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值