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

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

[Codeforces]860E Arkady and a Nobody-men相关推荐

  1. Codeforces 1326F Wise Men (容斥原理、状压 DP、划分数)

    题目链接 F1: https://codeforces.com/contest/1326/problem/F1 F2: https://codeforces.com/contest/1326/prob ...

  2. Codeforces 1326F Wise Men (容斥原理、状压 DP、子集和变换、划分数)

    题目链接 F1: https://codeforces.com/contest/1326/problem/F1 F2: https://codeforces.com/contest/1326/prob ...

  3. Codeforces Round #476 (Div. 2) C - Greedy Arkady

    Examples 1: input: 20 4 5 2 output: 8Examples 2: input: 30 9 4 1 output: 4 题意: n,k,m,d分别表示n颗糖,k个人,一次 ...

  4. Codeforces Round #476 (Div. 2) C. Greedy Arkady

    点击打开题目链接 知道了当第一个人比那 k-1 个人多分一次x就是最优策略 和 对分糖果的次数进行枚举就差不多了 当分i次的时候, 有 (i-1)*x(k-1)+x+i<=n; x=n/((i- ...

  5. CodeForces 375D Tree and Queries

    传送门:https://codeforces.com/problemset/problem/375/D 题意: 给你一颗有根树,树上每个节点都有其对应的颜色,有m次询问,每次问你以点v为父节点的子树内 ...

  6. 「日常训练」Bad Luck Island(Codeforces Round 301 Div.2 D)

    题意与分析(CodeForces 540D) 是一道概率dp题. 不过我没把它当dp做... 我就是凭着概率的直觉写的,还好这题不算难. 这题的重点在于考虑概率:他们喜相逢的概率是多少?考虑超几何分布 ...

  7. 【codeforces 812C】Sagheer and Nubian Market

    [题目链接]:http://codeforces.com/contest/812/problem/C [题意] 给你n个物品; 你可以选购k个物品;则 每个物品有一个基础价值; 然后还有一个附加价值; ...

  8. CodeForces 获得数据

    针对程序的输出可以看见 CodeForces :当输入.输出超过一定字符,会隐藏内容 所以:分若干个程序进行输入数据的获取 1. 1 for (i=1;i<=q;i++) 2 { 3 scanf ...

  9. codeforces水题100道 第二十七题 Codeforces Round #172 (Div. 2) A. Word Capitalization (strings)...

    题目链接:http://www.codeforces.com/problemset/problem/281/A 题意:将一个英文字母的首字母变成大写,然后输出. C++代码: #include < ...

  10. CodeForces 595A

    题目链接: http://codeforces.com/problemset/problem/595/A 题意: 一栋楼,有n层,每层有m户,每户有2个窗户,问这栋楼还有多少户没有睡觉(只要一个窗户灯 ...

最新文章

  1. 清华「男神」沈天成,踢毽子踢成了2021学生年度人物
  2. 企业网络高级技术第二章STP实验
  3. Android Studio +MAT 分析内存泄漏实战
  4. linux7下安装git,centos7下安装配置git仓库
  5. Linux vi 双屏显示,manjaro AwesomeWM 上使用双显示器
  6. HTML5 画布变换
  7. linux创建分区_在Linux中创建分区-分步指南
  8. Mac下载配置aria2和baidudl
  9. C语言面试基本点整理
  10. 人工智能基础(高中版)教材补充和资源分享之二 机器人学矩阵
  11. OneNote2013打开共享
  12. WDI面板数据(1990-2020)
  13. php windows挂掉,宕机是什么意思
  14. v-charts组件化示例及动态传参
  15. python 学习笔记之手把手讲解如何使用原生的 urllib 发送网络请求
  16. 大数据加工的方法,主要分为哪几种?
  17. 浪潮服务器开机没有信号输出,PLC输出指示灯已经点亮但是输出没有信号-工业支持中心-西门子中国...
  18. 前端面试题【131道】
  19. 对象、对象的属性、对象字面量、枚举对象中的属性、可变类型、变量和对象——JS对象
  20. mysql导入指定数据库_mysql命令行导入sql文件到指定数据库的方法

热门文章

  1. 30岁的职场危机,人生下半场的困局
  2. 伟创力被华为索赔数亿后发公开信:深感遗憾,仍希望能合作
  3. 这就是为什么IT人没有女朋友的原因!!
  4. 计算机网络负载均衡图片,负载均衡计算机网络课程网.ppt
  5. 精通~Scrum为什么会转型困难
  6. day12 装饰器的进阶
  7. Servlet 快速开始 表单中文字段
  8. windows server 2008 r2 x64 enterprise service pack1中aspjpeg.dll安装
  9. tesseract-ocr训练方法
  10. 码农干货系列【6】--javascript异步编程之:世界上最短的Promise库