[Codeforces]860E Arkady and a Nobody-men

  屯一个虚树的板子,顺便总结一下这样的题型。

Description

  给定一棵n个节点的有根树,在输入数据通过给出每个节点的父亲来表示这棵树。若某个节点的父亲为0,那么该节点即为根。现在对于每个点,询问它的每个祖先的所有深度不超过该节点的儿子的数量的总和。

Input

  第一行一个整数n。第二行n个整数,表示每个节点的父亲pi。

Output

  输出一行n个整数,表示每个节点的答案。

Sample Input

  5
  2 3 4 5 0

Sample Output

  10 6 3 1 0

HINT

  1<= n <=5*10^5,0<= pi <= n,保证有且仅有一个pi = 0。

 

Solution

  这道题的思路是显而易见的。

  对于每个节点,它的贡献是它到根的一条链(给它的所有祖先的子树大小+1)。

  每个节点的询问也是它到根的那一条链。

  至于深度不超过询问节点,我们发现,对于每个节点,只有深度小等于它的节点才对它有贡献,而且是一定有贡献。

  所以我们把所有点按深度排序,一层一层地做。插入一层,询问一层。

  用上树链剖分你就会在O(nlog2n)的时间内T掉该题。(如果你用树剖过了当我没说)

 

  然后我们考虑怎么将它优化。

  对于修改链的问题,我们通常可以通过逆向思考,将其变为子树修改,复杂度可以从O(nlog2n)降为O(nlogn)。

  询问很显然是很容易转化的,每个点都询问它的所有祖先,相当于每个点都对它的所有子节点加上答案贡献。

  而对于修改我们则要分析一下:

    

  如图,我们做到了第5层,计算红色节点对祖先子树大小的贡献。

  我们发现两个节点的贡献会在他们的lca处合并,那么这样一来正好构成一棵以该深度的点为叶节点的虚树!(即绿色节点)

  虚树上每个节点x到它父亲的这条链上被红色节点的贡献val[x]都是相等的,

  因此虚树上每个节点x对该节点的子节点们的贡献就是val[x]*(dep[x]-dep[fa[x]])!

  虚树的点数和叶节点数同级,所以总复杂度为O(nlogn)。

 

  然而通过观察,我们还可以发现每一层的节点答案对下一层是具有递推关系的。

  一个结点的答案可以由它父亲的答案加上该节点所在层的所有节点对它的贡献,这个我们同样可以用虚树解决。

  搞出虚树上每个节点被红色节点的贡献,然后从虚树根出发,往下dfs,到底层节点就计算一下答案即可。

  如果你用上tarjan求lca和一些骚排序(如果你愿意的话)可以把时间复杂度优化到O(n)。(你大可把这句话当成是小C在口胡)

 

  代码是第二种(用上线段树的)做法:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define ll long long
#define MN 500005
#define MS 20
using namespace std;
struct edge{int nex,to;}e[MN];
int dfbg[MN],dfed[MN],dep[MN],hr[MN],q[MN],siz[MN],fa[MS][MN],b[MN],u[MN];
ll t[MN],ans[MN];
vector <int> d[MN];
int dfn,pin,bin,tp,n,rt;

inline int read()
{
    int n=0,f=1; char c=getchar();
    while (c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
    while (c>='0' && c<='9') {n=n*10+c-'0'; c=getchar();}
    return n*f;
}

inline void ins(int x,int y) {e[++pin]=(edge){hr[x],y}; hr[x]=pin;}

void dfs(int x,int depth)
{
    dfbg[x]=++dfn; dep[x]=depth;
    d[depth].push_back(x);
    for (register int i=hr[x];i;i=e[i].nex)
        dfs(e[i].to,depth+1);
    dfed[x]=dfn;
}

int lca(int x,int y)
{
    register int i,k;
    if (dep[x]<dep[y]) swap(x,y);
    for (k=dep[x]-dep[y],i=0;k;k>>=1,++i)
        if (k&1) x=fa[i][x];
    if (x==y) return x;
    for (i=MS-1;i>=0;--i)
        if (fa[i][x]!=fa[i][y]) x=fa[i][x],y=fa[i][y];
    return fa[0][x];
}

inline void pushs(int x) {while (tp&&dfbg[x]>dfed[q[tp]]) --tp; if (tp) ins(q[tp],x); q[++tp]=x;}
inline int lowbit(int x) {return x&-x;}
inline void getadd(int x,int z) {for (;x<=n;x+=lowbit(x)) t[x]+=z;}
inline ll getsum(int x) {ll lt=0; for (;x;x-=lowbit(x)) lt+=t[x]; return lt;}

void dp(int x,int fat)
{
    siz[x]=u[x];
    for (register int i=hr[x];i;i=e[i].nex)
        dp(e[i].to,x),siz[x]+=siz[e[i].to];
    getadd(dfbg[x]+1, 1LL*siz[x]*(dep[x]-dep[fat]));
    getadd(dfed[x]+1,-1LL*siz[x]*(dep[x]-dep[fat]));
}

bool cmp(int x,int y) {return dfbg[x]<dfbg[y];}

int main()
{
    register int i,j;
    n=read(); bin=0;
    for (i=1;i<=n;++i) ins(fa[0][i]=read(),i),b[++bin]=i;
    for (i=1;i<=n;++i) if (!fa[0][i]) rt=i;
    for (i=1;i<MS;++i)
        for (j=1;j<=n;++j) fa[i][j]=fa[i-1][fa[i-1][j]];
    dfs(rt,1);
    for (i=2;d[i].size();++i)
    {
        for (j=1;j<=bin;++j) hr[b[j]]=u[b[j]]=0;
        tp=pin=bin=0;
        for (j=0;j<d[i].size();++j) ++u[b[++bin]=fa[0][d[i][j]]];    
        bin=unique(b+1,b+bin+1)-b-1;        
        for (j=1;j<bin;++j) b[bin+j]=lca(b[j],b[j+1]);
        bin=bin*2-1; sort(b+1,b+bin+1,cmp);
        bin=unique(b+1,b+bin+1)-b-1;
        for (j=1;j<=bin;++j) pushs(b[j]);
        dp(q[1],0);
        for (j=0;j<d[i].size();++j) ans[d[i][j]]=getsum(dfbg[d[i][j]]);    
    }
    for (i=1;i<=n;++i) printf("%I64d ",ans[i]);
}

 

Last Word

  感觉全程打得最难受的是倍增求lca,小C一直觉得自己的lca写得奇丑无比。

转载于:https://www.cnblogs.com/ACMLCZH/p/7551440.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在MATLAB中,NURBS(非均匀有理B样条)是一种强大的数学工具,用于表示和处理复杂的曲线和曲面。NURBS在计算机图形学、CAD(计算机辅助设计)、CAM(计算机辅助制造)等领域有着广泛的应用。下面将详细探讨MATLAB中NURBS的绘制方法以及相关知识点。 我们需要理解NURBS的基本概念。NURBS是B样条(B-Spline)的一种扩展,其特殊之处在于引入了权重因子,使得曲线和曲面可以在不均匀的参数空间中进行平滑插值。这种灵活性使得NURBS在处理非均匀数据时尤为有效。 在MATLAB中,可以使用`nurbs`函数创建NURBS对象,它接受控制点、权值、 knot向量等参数。控制点定义了NURBS曲线的基本形状,而knot向量决定了曲线的平滑度和分布。权值则影响曲线通过控制点的方式,大的权值会使曲线更靠近该点。 例如,我们可以使用以下代码创建一个简单的NURBS曲线: ```matlab % 定义控制点 controlPoints = [1 1; 2 2; 3 1; 4 2]; % 定义knot向量 knotVector = [0 0 0 1 1 1]; % 定义权值(默认为1,如果未指定) weights = ones(size(controlPoints,1),1); % 创建NURBS对象 nurbsObj = nurbs(controlPoints, weights, knotVector); ``` 然后,我们可以用`plot`函数来绘制NURBS曲线: ```matlab plot(nurbsObj); grid on; ``` `data_example.mat`可能包含了一个示例的NURBS数据集,其中可能包含了控制点坐标、权值和knot向量。我们可以通过加载这个数据文件来进一步研究NURBS的绘制: ```matlab load('data_example.mat'); % 加载数据 nurbsData = struct2cell(data_example); % 转换为cell数组 % 解析数据 controlPoints = nurbsData{1}; weights = nurbsData{2}; knotVector = nurbsData{3}; % 创建并绘制NURBS曲线 nurbsObj = nurbs(controlPoints, weights, knotVector); plot(nurbsObj); grid on; ``` MATLAB还提供了其他与NURBS相关的函数,如`evalnurbs`用于评估NURBS曲线上的点,`isoparm`用于生成NURBS曲面上的等参线,以及`isocurve`用于在NURBS曲面上提取特定参数值的曲线。这些工具对于分析和操作NURBS对象非常有用。 MATLAB中的NURBS功能允许用户方便地创建、编辑和可视化复杂的曲线和曲面。通过对控制点、knot向量和权值的调整,可以精确地控制NURBS的形状和行为,从而满足各种工程和设计需求。通过深入理解和熟练掌握这些工具,可以在MATLAB环境中实现高效的NURBS建模和分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值