NC201400 树学题解

一.题解

? 这道题又是一道换根dp板子题,代码结构与 Accumulation Degree 这道题基本一致,唯一不同的就是转移了【不过转移的时候,因为方程的原因不需要特殊考虑叶节点】

? 我们先套路的设\(dp[i]\)表示以\(i\)为根的子树中所有点的深度和,现在,我们来想想转移。

? 我们发现,如果我们要从\(i\)的一个儿子v转移到i的话,以v为根的子树中的所有节点的深度都加了1,现在我们就不能够直接求值了,怎么办呢?很简单,我们发现,既然以v为根的子树中的所有节点的深度都加了1,那么,一共增加的值,不就正好是以v为根的子树中节点的个数嘛?

? 所以,我们只需要再维护一个以i为根的子树的大小\(siz[i]\)即可

? 那么,我们就可以很简单的推出转移:

? \(dp[i]=\)\(\sum_{v\in son(i)}(dp[v]+siz[v])\)

? \(siz[i]=\)\(1(i本身)+\sum_{v\in son(i)}siz[v]\)

? 这样,我们一遍dfs就可以求出所有\(dp[i]\)了!

? 现在,我们考虑换根dp

? 我们假设将根\(x\)换成了它的一个儿子\(y\),那么,同样的,改变的只是\(x\)和\(y\)的子树,我们将这个"影响"修改一下即可~

? 首先,\(dp[i]-=(dp[v]+siz[v])\)(减去v的贡献)

? 然后,\(siz[i]-=siz[v],siz[v]+=siz[i]\)(修改两个点的子树大小)

? 最后,\(dp[v]+=(dp[i]+siz[i])\)(加上i的贡献)

? 为什么是这个顺序呢?因为修改\(dp[i]\)的时候,\(dp[v]\)和\(siz[v]\)必须和以前一样(因为我们是从这两个值转移过来的),然后要修改\(dp[v]\)的话,\(dp[i]\)和\(siz[i]\)又必须是修改后,没加v的贡献的值,所以,这样安排修改顺序是很好的。(当然,你非要用其他顺序的话,我也无话可说,只是要注意修改的方法有可能会不同)

? 最后,我们每次统计下以任意点为根时,\(dp[i]\)的值的最小值即可~

? 代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+1;
struct node{
    int v,nex;
}t[N<<1];
int dp[N],siz[N];
int las[N],len,ans=1e17;
inline void add(int u,int v){
    t[++len]=(node){v,las[u]},las[u]=len;
}
inline void dfs1(int now,int fa){
    siz[now]=1;
    for(int i=las[now];i;i=t[i].nex){
        int v=t[i].v;
        if(v!=fa){
            dfs1(v,now);
            siz[now]+=siz[v];
            dp[now]+=(dp[v]+siz[v]);
        }
    }
}
inline void dfs2(int now,int fa){
    ans=min(ans,dp[now]);
    for(int i=las[now];i;i=t[i].nex){
        int v=t[i].v;
        if(v!=fa){
            int pas1=siz[now],pas2=siz[v];
            int Pas1=dp[now],Pas2=dp[v];//继续偷懒
            dp[now]-=(dp[v]+siz[v]);
            siz[now]-=siz[v];siz[v]+=siz[now];
            dp[v]+=(dp[now]+siz[now]);
            dfs2(v,now);
            siz[now]=pas1,siz[v]=pas2;
            dp[now]=Pas1,dp[v]=Pas2;
        }
    }
}
signed main(){
    int n;
    scanf("%lld",&n);
    for(int i=1;i<n;++i){
        int u,v;
        scanf("%lld%lld",&u,&v);
        add(u,v),add(v,u);
    }
    dfs1(1,1);dfs2(1,1);
    printf("%lld",ans);
    return 0;
}

二.闲话

? 其实一开始,我变量都开的是int,然而,交上去后,算了下,每次的贡献最大可以达到\(n^2\)水平【链】,于是发现很可能爆int,于是,我马上改成long long,然后,改完后正准备提交。。。

? 恭喜通过~

? 我:???

原文地址:https://www.cnblogs.com/ThinkofBlank/p/12690211.html

时间: 2024-08-30 14:45:11

NC201400 树学题解的相关文章

CODEVS 1080 线段树练习 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:http://codevs.cn/problem/1080/ 题目描述 Description 一行N个方格,开始每个格子里都有一个整数.现在动态地提出一些问题和修改:提问的形式是求某一个特定的子区间[a,b]中所有元素的和:修改的规则是指定某一个格子x,加上或者减去一个特定的值A.现在要求你能对每个提问作出正确的回答.1≤N<100000,,提问和修改的总数m<10000条. 输入描述 Input Descrip

QAQ的LIS树 QAQ的LIS树2 题解报告

这两道题实际上考试的时候是一道题OwO 太可怕了,忙了我三个多小时,写了整整7K 这个题两个询问关联性不强,所以分开来考虑 QAQ的LIS树 考虑如何用dp求解答案 设dp(v)表示v到根的修改后的序列的和,c(v)是v点点权 那么v的答案就是dp(v)减去v到根的点权和 最直观的想法是我们从v点向上暴力跳父亲,跳到第一个不用修改的点u 不难发现因为u没有修改,所以之后的序列和u之后的序列是完全一样的 可以直接转移给v了,那么dp(v)的值就是dp(u)和v->u的序列和了 注意到v->u的所

Kuangbin 带你飞-线段树专题 题解

HDU 1166 敌兵布阵 单调更新区间查询和 #include <map> #include <set> #include <list> #include <cmath> #include <ctime> #include <deque> #include <stack> #include <queue> #include <cctype> #include <cstdio> #inc

HDU 5992 Finding Hotels(KD树)题解

题意:n家旅店,每个旅店都有坐标x,y,每晚价钱z,m个客人,坐标x,y,钱c,问你每个客人最近且能住进去(非花最少钱)的旅店,一样近的选排名靠前的. 思路:KD树模板题 代码: #include<set> #include<map> #include<stack> #include<cmath> #include<queue> #include<vector> #include<cstdio> #include<c

HDU 4825 Xor Sum(01字典树)题解

思路:先把所有数字存进字典树,然后从最高位贪心. 代码: #include<set> #include<map> #include<stack> #include<cmath> #include<queue> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm>

HDU 5919 Sequence II(主席树)题解

题意:有A1 ~ An组成的数组,给你l r,L = min((l + ans[i - 1]) % n + 1, (r + ans[i - 1]) % n + 1),R = max((l + ans[i - 1]) % n + 1, (r + ans[i - 1]) % n + 1),你先需要的到L,R区间有k个不同的数字,然后问你L,R区间第(k + 1)/ 2个不同的数字下标是多少? 思路:显然是个在线询问. 我们之前已经会用主席树求区间内有多少不同数字了:从左到右把每个数字的位置存进每个操

HDU 6155 Subsequence Count(矩阵 + DP + 线段树)题解

题意:01串,操作1:把l r区间的0变1,1变0:操作2:求出l r区间的子序列种数 思路:设DP[i][j]为到i为止以j结尾的种数,假设j为0,那么dp[i][0] = dp[i - 1][1] + dp[i -1][0] (0结尾新串) + dp[i - 1][0] (0结尾旧串) - dp[i - 1][0] (重复) + 1(0本身被重复时去除). 那么可以得到转移时的矩阵 $$ \left( \begin{matrix} dp[i - 1][0] & dp[i - 1][1] &am

FZU2105 Digits Count(按位建线段树)题解

题意: 给出区间与.或.异或\(x\)操作,还有询问区间和. 思路: 因为数比较小,我们给每一位建线段树,这样每次只要更新对应位的答案. 与\(0\)和或\(1\)相当于重置区间,异或\(1\)相当于翻转区间,那么设出两个\(lazy\)搞一下.注意父区间\(pushdown\)重置标记时,子区间的翻转标记要清空. 代码: #include <map> #include <set> #include <queue> #include <cmath> #inc

bzoj4355 Play with sequence(吉司机线段树)题解

题意: 已知\(n\)个数字,进行以下操作: \(1.\)区间\([L,R]\) 赋值为\(x\) \(2.\)区间\([L,R]\) 赋值为\(max(a[i] + x, 0)\) \(3.\)区间\([L,R]\) 询问\(0\)个数 已知初始值\(\geq 0\),\(x\geq0\). 思路: 吉司机线段树. 操作\(1\)可以直接打覆盖标记. 操作\(2\)可以分为两步:区间加\(x\),然后取区间\(max(a[i],0)\). 操作\(3\)只要维护最小值的个数,因为不管怎么操作最