zoukankan      html  css  js  c++  java
  • AtCoder AGC029E Wandering TKHS

    题目链接

    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;
    }
    
  • 相关阅读:
    Springboot 报错 Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
    layui时间戳转日期踩坑
    前端 传入Date 为空的解决办法
    解决springdatajpa设置默认值保存null无效的问题
    2020-04-12工作记录
    js原型链继承的傻瓜式详解
    一个关于python装饰器参数的问题
    【转】Unicode utf8等编码类型的原理
    c/c++内存泄露的检测方法
    【转】什么是动态规划?动态规划的意义是什么
  • 原文地址:https://www.cnblogs.com/suncongbo/p/12268850.html
Copyright © 2011-2022 走看看