[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写得奇丑无比。

时间: 2024-11-08 21:11:40

[Codeforces]860E Arkady and a Nobody-men的相关文章

codeforces 591B Rebranding (模拟)

Rebranding Problem Description The name of one small but proud corporation consists of n lowercase English letters. The Corporation has decided to try rebranding - an active marketing strategy, that includes a set of measures to change either the bra

Codeforces Round #315 (Div. 1)

A. Primes or Palindromes? time limit per test 3 seconds memory limit per test 256 megabytes input standard input output standard output Rikhail Mubinchik believes that the current definition of prime numbers is obsolete as they are too complex and un

三元逆序对 求i&lt;j&lt;k &amp;&amp; a[i]&gt;a[j]&gt;a[k] 的对数 树状数组Codeforces 61E Enemy is weak

http://codeforces.com/problemset/problem/61/E E. Enemy is weak time limit per test 5 seconds memory limit per test 256 megabytes input standard input output standard output The Romans have attacked again. This time they are much more than the Persian

Codeforces Amusing Joke 题解

So, the New Year holidays are over. Santa Claus and his colleagues can take a rest and have guests at last. When two "New Year and Christmas Men" meet, thear assistants cut out of cardboard the letters from the guest's name and the host's name i

Playrix Codescapes Cup (Codeforces Round #413, rated, Div. 1 + Div. 2) C. Fountains 【树状数组维护区间最大值】

题目传送门:http://codeforces.com/contest/799/problem/C C. Fountains time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Arkady plays Gardenscapes a lot. Arkady wants to build two new fountains. The

【codeforces 718E】E. Matvey&#39;s Birthday

题目大意&链接: http://codeforces.com/problemset/problem/718/E 给一个长为n(n<=100 000)的只包含‘a’~‘h’8个字符的字符串s.两个位置i,j(i!=j)存在一条边,当且仅当|i-j|==1或s[i]==s[j].求这个无向图的直径,以及直径数量. 题解:  命题1:任意位置之间距离不会大于15. 证明:对于任意两个位置i,j之间,其所经过每种字符不会超过2个(因为相同字符会连边),所以i,j经过节点至多为16,也就意味着边数至多

Codeforces 124A - The number of positions

题目链接:http://codeforces.com/problemset/problem/124/A Petr stands in line of n people, but he doesn't know exactly which position he occupies. He can say that there are no less than a people standing in front of him and no more than b people standing b

Codeforces 841D Leha and another game about graph - 差分

Leha plays a computer game, where is on each level is given a connected graph with n vertices and m edges. Graph can contain multiple edges, but can not contain self loops. Each vertex has an integer di, which can be equal to 0, 1 or  - 1. To pass th

Codeforces Round #286 (Div. 1) A. Mr. Kitayuta, the Treasure Hunter DP

链接: http://codeforces.com/problemset/problem/506/A 题意: 给出30000个岛,有n个宝石分布在上面,第一步到d位置,每次走的距离与上一步的差距不大于1,问走完一路最多捡到多少块宝石. 题解: 容易想到DP,dp[i][j]表示到达 i 处,现在步长为 j 时最多收集到的财富,转移也不难,cnt[i]表示 i 处的财富. dp[i+step-1] = max(dp[i+step-1],dp[i][j]+cnt[i+step+1]) dp[i+st