[JZOJ4567]nekopara

本文介绍了一道关于树结构的算法题,利用树剖分技术离线处理路径上的边权查询,通过预处理和区间合并实现了高效的解答。

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

题目大意

一棵树,有n个节点,边有权值li。给定数组a
q个询问,每次询问给出一条路径的起点、终点和lim。你要将路径展开,设某个点i前面连续l大于lim的边的数量为e(i)。求

x on pathae(x)

1n,q105,ai103,li,l109


题目分析

乍一看题意很绕,无从下手。考虑到有边权大小的限制,如果在线可能很难处理(函数式线段树???)。
那我们就离线,把边和询问放一块儿降序排序,按顺序处理询问以及插入,那么已经插入的边就是超过询问的lim的。
树剖维护区间左右两段连续的有边的长度以及答案,然后合并一下即可。
细节见代码实现。
时间复杂度O((n+q)log2n)


代码实现

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cmath>

using namespace std;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}

const int N=100050;
const int M=N<<1;
const int Q=100050;
const int S=N+Q;
const int EL=N<<1;
const int LGEL=18;

int last[N],fa[N],high[N],pos[N],a[N],hea[N],size[N],DFN[N],prt[N];
int n,q,tot,el,idx,lgel,s;
int tov[M],next[M];
int rmq[EL][LGEL];
int euler[EL];
int ans[Q];

struct D
{
    int u,v,li,og;
}op[S];

bool operator<(D x,D y){return x.li>y.li||x.li==y.li&&x.og>y.og;}

struct L
{
    int l,r,ans,size;

    L (int l0=0,int r0=0,int ans0=0,int size0=0){l=l0,r=r0,ans=ans0,size=size0;}
};

L operator+(L x,L y)
{
    L ret;
    ret.size=x.size+y.size;
    ret.l=x.l+(x.l==x.size?y.l:0);
    ret.r=y.r+(y.r==y.size?x.r:0);
    ret.ans=x.ans+y.ans-a[x.r]-a[y.l]+a[x.r+y.l];
    return ret;
}

struct segment_tree
{
    L v[N<<2];

    void build(int x,int l,int r)
    {
        if (l==r)
        {
            v[x].size=1;
            return;
        }
        int mid=l+r>>1;
        build(x<<1,l,mid),build(x<<1|1,mid+1,r);
        v[x].size=v[x<<1].size+v[x<<1|1].size;
    }

    void modify(int x,int y,int l,int r)
    {
        if (l==r)
        {
            v[x].ans=a[v[x].l=v[x].r=1];
            return;
        }
        int mid=l+r>>1;
        if (y<=mid) modify(x<<1,y,l,mid);
        else modify(x<<1|1,y,mid+1,r);
        v[x]=v[x<<1]+v[x<<1|1];
    }

    L query(int x,int st,int en,int l,int r)
    {
        if (st==l&&en==r) return v[x];
        int mid=l+r>>1;
        if (en<=mid) return query(x<<1,st,en,l,mid);
        else if (mid+1<=st) return query(x<<1|1,st,en,mid+1,r);
        else return query(x<<1,st,mid,l,mid)+query(x<<1|1,mid+1,en,mid+1,r);
    }
}t;

void insert(int x,int y)
{
    tov[++tot]=y,next[tot]=last[x],last[x]=tot;
}

void dfs(int x)
{
    size[euler[++el]=x]=1,pos[x]=el;
    for (int i=last[x],y;i;i=next[i])
        if ((y=tov[i])!=fa[x])
        {
            fa[y]=x,high[y]=high[x]+1,dfs(y),size[euler[++el]=x]+=size[y];
            if (!hea[x]||size[hea[x]]<size[y]) hea[x]=y;
        }
}

void pre()
{
    lgel=trunc(log(el)/log(2));
    for (int i=1;i<=el;i++) rmq[i][0]=euler[i];
    for (int j=1;j<=lgel;j++)
        for (int i=1;i+(1<<j)-1<=el;i++)
            if (high[rmq[i][j-1]]<high[rmq[i+(1<<j-1)][j-1]])
                rmq[i][j]=rmq[i][j-1];
            else
                rmq[i][j]=rmq[i+(1<<j-1)][j-1];
}

void split(int x,int z)
{
    DFN[x]=++idx,prt[x]=z;
    if (hea[x]) split(hea[x],z);
    else return;
    for (int i=last[x],y;i;i=next[i])
        if ((y=tov[i])!=fa[x]&&y!=hea[x]) split(y,y);
}

int getrmq(int l,int r)
{
    int lgr=trunc(log(r-l+1)/log(2));
    if (high[rmq[l][lgr]]<high[rmq[r-(1<<lgr)+1][lgr]]) return rmq[l][lgr];
    else return rmq[r-(1<<lgr)+1][lgr];
}

int lca(int x,int y)
{
    if ((x=pos[x])>(y=pos[y])) swap(x,y);
    return getrmq(x,y);
}

int getans(int x,int y)
{
    int z=lca(x,y),f;
    L ret1=L(),ret2=L();
    for (f=prt[x];f!=prt[z];x=fa[f],f=prt[x]) ret1=t.query(1,DFN[f],DFN[x],1,idx)+ret1;
    if (z!=x) ret1=t.query(1,DFN[z]+1,DFN[x],1,idx)+ret1;
    for (f=prt[y];f!=prt[z];y=fa[f],f=prt[y]) ret2=t.query(1,DFN[f],DFN[y],1,idx)+ret2;
    if (z!=y) ret2=t.query(1,DFN[z]+1,DFN[y],1,idx)+ret2;
    return ret1.ans+ret2.ans-a[ret1.l]-a[ret2.l]+a[ret1.l+ret2.l];
}

void change(int x,int y)
{
    if (fa[x]!=y) swap(x,y);
    t.modify(1,DFN[x],1,idx);
}

int main()
{
    freopen("nekopara.in","r",stdin),freopen("nekopara.out","w",stdout);
    n=read();
    for (int i=1;i<n;i++) a[i]=read();
    for (int i=1;i<n;i++)
    {
        op[i].u=read()+1,op[i].v=read()+1,op[i].li=read();
        insert(op[i].u,op[i].v),insert(op[i].v,op[i].u);
    }
    dfs(1),pre(),split(1,1);
    t.build(1,1,idx);
    q=read();
    for (int i=1;i<=q;i++) op[i+n-1].u=read()+1,op[i+n-1].v=read()+1,op[i+n-1].li=read(),op[i+n-1].og=i;
    s=n-1+q;
    sort(op+1,op+1+s);
    for (int i=1;i<=s;i++)
        if (op[i].og)
            ans[op[i].og]=getans(op[i].u,op[i].v);
        else
            change(op[i].u,op[i].v);
    for (int i=1;i<=q;i++) printf("%d\n",ans[i]);
    fclose(stdin),fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值