码迷,mamicode.com
首页 > 其他好文 > 详细

AtCoder AGC029E Wandering TKHS

时间:2020-02-06 16:28:13      阅读:98      评论:0      收藏:0      [点我收藏+]

标签:tin   包括   ==   时间复杂度   http   math   现在   后缀   int   

题目链接

https://atcoder.jp/contests/agc029/tasks/agc029_e

题解

写了一半发现假了然后强行乱改一通改对了……
我们用“\(u\)子树内小于\(x\)的连通块”来表示\(u\)子树内到\(u\)路径上的点都小于\(x\)的点(包括\(u\))的集合,集合的大小用\(C(u,x)\)表示。
考虑这个游走的过程,设点\(u\)到根的路径上分别是\(u=u_1,u_2,u_3,...,u_{k-1},u_k=1\), 则对于\(i\)来说干的事情是“把\(u_i\)子树内小于\(u_{i+1}\)的连通块都走一遍”。那么对于一个\(i\)来说若存在\(j>i\)\(u_{j+1}>u_{i+1}\), 那么后者的作用会包含前者,前者无用。也就是说我们要考虑根到每个点路径上的前缀最大值(就是每个点到根路径上的后缀最大值)。
考虑递推,从父亲递推到儿子。设\(mx[u]\)表示\(u\)到根路径上的最大值,\(v\)\(u\)的儿子,经过简单推导可得如下递推式:

ans[v] = ans[u];
if(u>mx[fa[u]]) {ans[v] += C(v,mx[u])-C(v,mx[fa[u]]);} //u是前缀最大值点
if(v>mx[u]) {ans[v] += 所有v的儿子v'的C(v',mx[u])之和+1;} //v是前缀最大值点

第三行是因为v>mx[u]所以v的子树不被包括在上一次的最大值点计算的贡献中,需要重新计算。
(我知道这样讲很不清楚……可是抱歉博主实在是不知道如何用文字把这个推导过程写清楚,并且这个实际上也挺简单的)
现在考虑如何对每个前缀最大值的\(u\)的每个儿子\(v\)求出\(C(v,mx[u])\)\(C(v,mx[fa[u]])\). 看起来需要数据结构,但是我们发现所有这些值的总和是\(O(n)\)级别的,所以暴力枚举就可以了。
时间复杂度\(O(n)\).

代码

#include<bits/stdc++.h>
#define llong long long
#define mkpr make_pair
#define riterator reverse_iterator
using namespace std;

inline int read()
{
    int x = 0,f = 1; char ch = getchar();
    for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
    for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
    return x*f;
}

const int N = 2e5;
struct Edge
{
    int nxt,v;
} e[(N<<1)+3];
int fe[N+3];
int fa[N+3];
int mx[N+3];
int f[N+3],g[N+3],h[N+3];
int ans[N+3];
int n,en;

void addedge(int u,int v)
{
    en++; e[en].v = v;
    e[en].nxt = fe[u]; fe[u] = en;
}

int dfs2(int u,int x)
{
    if(u>x) return 0;
    int ret = 1;
    for(int i=fe[u]; i; i=e[i].nxt)
    {
        int v = e[i].v; if(v==fa[u]) continue;
        ret += dfs2(v,x);
    }
    return ret;
}

void dfs1(int u)
{
    for(int i=fe[u]; i; i=e[i].nxt)
    {
        int v = e[i].v; if(v==fa[u]) continue;
        fa[v] = u;
        mx[v] = max(mx[u],v);
        dfs1(v);
    }
    if(u>mx[fa[u]])
    {
        h[u] = 1;
        for(int i=fe[u]; i; i=e[i].nxt)
        {
            int v = e[i].v; if(v==fa[u]) continue;
            f[v] = dfs2(v,u);
            g[v] = dfs2(v,mx[fa[u]]); h[u] += g[v];
        }
    }
}

void dfs3(int u)
{
    for(int i=fe[u]; i; i=e[i].nxt)
    {
        int v = e[i].v; if(v==fa[u]) continue;
        ans[v] = ans[u]; if(u>mx[fa[u]]) {ans[v] += f[v]-g[v];} if(mx[v]>mx[u]) {ans[v] += h[v];}
        dfs3(v);
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=1; i<n; i++)
    {
        int u,v; scanf("%d%d",&u,&v);
        addedge(u,v); addedge(v,u);
    }
    mx[1] = 1; f[1] = 1; dfs1(1);
//  printf("f: "); for(int i=1; i<=n; i++) printf("%d ",f[i]); puts("");
//  printf("g: "); for(int i=1; i<=n; i++) printf("%d ",g[i]); puts("");
//  printf("h: "); for(int i=1; i<=n; i++) printf("%d ",h[i]); puts("");
    dfs3(1);
    for(int i=2; i<=n; i++) printf("%d ",ans[i]); puts("");
    return 0;
}

AtCoder AGC029E Wandering TKHS

标签:tin   包括   ==   时间复杂度   http   math   现在   后缀   int   

原文地址:https://www.cnblogs.com/suncongbo/p/12268850.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!