[POI][BZOJ4381]Odwiedziny 访问

题目大意

给定一课 N 个节点数,有M个询问,询问从 u 点每次跳k个点到 v 点经过的点权和。


f u,v 的lca,那么 u v的路径可以分成 u f再到 v

对于每一个节点x,令 g(x,k) 为从 x 每次跳k步到根节点的点权和, fa(x,k) x 往上走k步的祖先,其中 k<=n ,那么如果步伐 k<=n ,答案就可以 O(1) 得出,如果 k>n ,那么可以每次从 x 点往上跳n步,顺便记录答案,操作次数不会大于 n

O(m(n+logn))

#include<cstdio>
#include<cmath>
#define N 50005
#define M 235
int up[N][M],a[N],b[N],c[N],n,en,h[N],x,y,f[N],li[N],l,block,sz[N],son[N],top[N],dep[N],nx[N][M];
struct edge{int next,x;}e[N*2];
inline void ins(int a,int b){e[++en].x=b;e[en].next=h[a];h[a]=en;}

inline void read(int &x)
{
    char ch=C();x=0;int f=1;
    for(;ch>'9'||ch<'0';ch=C())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';x=(x<<1)+(x<<3)+ch-'0',ch=C());x*=f;
}
void dfs1(int x)
{
    sz[x]=1;li[++l]=x;dep[x]=dep[f[x]]+1;
    for(int i=h[x];i;i=e[i].next)
    if(e[i].x!=f[x])
    {
        f[e[i].x]=x;dfs1(e[i].x);
        if(sz[e[i].x]>sz[son[x]])son[x]=e[i].x;
        sz[x]+=sz[e[i].x];
    }
}
void dfs2(int x,int tp)
{
    top[x]=tp;
    if(son[x])dfs2(son[x],tp);
    for(int i=h[x];i;i=e[i].next)
    if(e[i].x!=f[x]&&e[i].x!=son[x]) dfs2(e[i].x,e[i].x);
}
inline int father(int x,int y)
{
    while(y>=block) x=nx[x][block],y-=block;
    return nx[x][y];
}
inline int calc(int a,int b,int c)
{
    int x=a,y=b,ans=0,l=0,s,lca;
    while(top[x]!=top[y])
    if(dep[top[x]]<dep[top[y]]) y=f[top[y]]; else x=f[top[x]];
    if(dep[x]>dep[y]) lca=y;else lca=x;
    if(dep[a]+dep[b]-2*dep[lca]<=c) return up[a][0]+up[b][0];
    if(c<=block)
    {
        if((dep[a]+dep[b]-2*dep[lca])%c==0) ans-=up[b][0];
        if(lca==a) return ans+up[nx[b][(dep[b]-dep[a])%c]][c]-up[nx[a][c]][c]+up[b][0];
        if(lca==b) return ans+up[a][c]-up[father(a,(dep[a]-dep[b])/c*c+c)][c]+up[b][0];
        ans+=up[a][c]-up[father(a,(dep[a]-dep[lca])/c*c+c)][c];
        ans+=up[father(b,(dep[b]+dep[a]-2*dep[lca])%c)][c]-up[father(lca,(dep[a]-dep[lca])%c)][c]+up[b][0];
        return ans;
    }
    if(lca==a)
    {
        ans=up[b][0];
        if((dep[b]+dep[a]-2*dep[lca])%c==0) ans-=up[b][0];
        b=father(b,(dep[b]-dep[a])%c);
        while(dep[b]>=dep[a]) ans+=up[b][0],b=father(b,c);
        return ans;
    }
    if(lca==b)
    {
        if((dep[b]+dep[a]-2*dep[lca])%c==0) ans-=up[b][0];
        while(dep[a]>=dep[b]) ans+=up[a][0],a=father(a,c);
        return ans+up[b][0];
    }
    ans=up[b][0];
    if((dep[b]+dep[a]-2*dep[lca])%c==0) ans-=up[b][0];
    if((dep[a]-dep[lca])%c==0) ans-=up[lca][0];
    b=father(b,(dep[b]+dep[a]-dep[lca]*2)%c);
    while(dep[a]>=dep[lca])ans+=up[a][0],a=father(a,c);
    while(dep[b]>=dep[lca])ans+=up[b][0],b=father(b,c);
    return ans;
}
int main()
{
    read(n);
    for(int i=1;i<=n;i++) read(a[i]),up[i][0]=a[i],nx[i][0]=i;
    for(int i=1;i<n;i++) read(x),read(y),ins(x,y),ins(y,x);
    for(int i=1;i<=n;i++) read(b[i]);
    for(int i=1;i<n;i++) read(c[i]);
    dfs1(1);dfs2(1,1);block=(int)sqrt(n);
    for(int i=1;i<=l;i++)
    for(int j=1;j<=block;j++)
    nx[li[i]][j]=nx[f[li[i]]][j-1],up[li[i]][j]=up[nx[li[i]][j]][j]+a[li[i]];
    for(int i=1;i<n;i++)
    printf("%d\n",calc(b[i],b[i+1],c[i]));
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值