AtCoder AGC029E Wandering TKHS

2024-02-15 15:32
文章标签 atcoder wandering agc029e tkhs

本文主要是介绍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;
}

这篇关于AtCoder AGC029E Wandering TKHS的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/711786

相关文章

AtCoder Beginner Contest 370 Solution

A void solve() {int a, b;qr(a, b);if(a + b != 1) cout << "Invalid\n";else Yes(a);} B 模拟 void solve() {qr(n);int x = 1;FOR(i, n) FOR(j, i) qr(a[i][j]);FOR(i, n) x = x >= i ? a[x][i]: a[i][x];pr2(

AtCoder Beginner Contest 369 D - Bonus EXP 动态规划

原题链接: https://atcoder.jp/contests/abc369/tasks/abc369_d 思路:   这道题为什么要用动态规划呢,其实,对于第i个怪物,我们有打与不打两种处理方式,而对于打,我们是获得两倍的经验值,还是一倍的经验值,与我们打了奇数只怪物还是打了偶数只怪物有关了,因此我们定义dp[i][0] 为前i只怪物总共打了偶数次,dp[i][1] 为前i只怪物总

题解AtCoder ABC 358 F Easiest Maze

一道模拟题。 思路 最短的路线是直接竖着走下来,经过 n n n 个格子,所以 k k k 最小是 n n n。如果想要延长路线,可以采用九转大肠的形状,就像这样: 可以发现,每次向左走之后都必须走回来,所以每次新经过的格子数是偶数,得到 k − n k-n k−n 是偶数才有可行的方案。 首先,把整张图表的初始状态设为如下形式(即每个格点都是独立的): +++++S++o|o|o

AtCoder Beginner Contest 369 ABCDE

背景 无 A题:369  思路 假设A<=B 分类讨论,有如下两种情况         1.A==B,情况唯一,另外一个数只能取A         2.A<B,首先我们可以以B-A为公差d构造,另外一个数可以取A-d或者B+d。(然后接着考虑放在A和B中间的情况,样例中给了,只要B-A为偶数即可) 代码 inline void solve() {int a, b; cin >>

AtCoder Beginner Contest 369 A~E

封面原图 画师かにょこ AtCoder Beginner Contest 369 我愿称之为等差数列场 A - 369 题意 给两个数,问能和他们构成等差数列的数有多少个 代码 #include <bits/stdc++.h>#define mod 998244353using namespace std;typedef long long ll;typed

AtCoder Beginner Contest 366(D~E题解)

闲来无事去vp了一下之前放假没打的比赛,感觉需要总结的也就这两题吧,a,c都是水题,b只不过是实现有一点难,并不是很难写,d是一个需要自己推的三维前缀和,e也是一种前缀和,我当时没想到,看了大犇的代码才知道还能这么做 D - Cuboid Sum Query 题意:给你一个三维数组,然后给你q次询问,每次询问有一个起始位置和终止位置,然后问你这个的三维前缀和是什么 思路:用容斥原理推出三

AtCoder Beginner Contest 368 题解思路(A-D,F)

AtCoder Beginner Contest 368 题解&&思路(A-D,F) A - Cut 题目描述 有 N N N 个数在一个桶里面,从上往下第 i i i 个数是 A i A_i Ai​,从桶下面取出 K K K 个数,保持原顺序放在桶的上面,从上到下打印写在卡片上的整数。 思路 时间复杂度 O ( N ) . \mathcal{O}(N). O(N). 本质上

Hitachi Vantara Programming Contest 2024(AtCoder Beginner Contest 368)ABCDEF

前言 F比E简单多了,难评 A题:Cut 题意 给定n张卡片,将后m张卡片按顺序放置到顶部,按顺序输出卡片id 思路 更改输出方式即可 代码 inline void solve() {int n, m; cin >> n >> m;vector<int> a(n + 1);for (int i = 1; i <= n; i ++ ) cin >> a[i];for (int i =

D - Pedometer AtCoder Beginner Contest 367

题意: 一个长度为n的数组a首尾相接,求满足a[i]~a[j]的和是m的倍数的[i,j]对数 思路: 由于首位相接,那么区间i-->j的所有数有两种情况:第一种是i<j的情况,第二种是i>j的情况 为了简化处理,我们可以将他变为一个线性数组,也就是原数组的后面再加一个原数组,那么新数组的所有区间就包含了以上两种情况: 设s[i]为前缀和数组,a[i]到a[j]的数的和是m的倍数-

Atcoder - 4142 尺取法,位运算(适合难度:普及+/提高-)

Atcoder - 4142 尺取法,位运算(适合难度:普及+/提高-) 异或不懂的参考位运算 if a ^ b ^ c < a + b + c 说明a ^ b <= a + b \qquad 枚举一个左端点,然后利用双指针计俩来滑动右端点来找到最大的满足条件的右端点。解法和UVA1121是很相似的,都是尺取法。 #include <iostream>#include <