例题1
题目大意:给定n个节点的6,找到一个节点,使得所有点的深度之和最大
解题思路:先以节点一为根节点,求出所有点的子树(包含自身)的节点数是多少,用size数组记录。然后是状态转移,从根节点u换到邻接的点v,f[v]=f[u]+(size[1]-size[v])-size[v],以v为根的子树的深度减一,其它的点深度加一
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PLL;
const int N=1e6+100;
#define int long long
ll n;
vector<ll> Edge[N];
ll size[N];
ll depth[N];// 算出以一为根节点 每个点的深度是多少
ll all_depth;
ll f[N];
ll ans;
ll idx;
ll dfs1(ll u,ll fa)
{
depth[u]=depth[fa]+1;
all_depth+=depth[u];
ll res=1;
for(auto T_T:Edge[u])
{
if(T_T==fa)
continue;
res+= dfs1(T_T,u);
}
return size[u]=res;
}
void dfs2(ll u,ll fa)
{
for(auto T_T:Edge[u])
{
if(fa==T_T)
continue;
f[T_T]=f[u]+(size[1]-size[T_T])-size[T_T];
ans=max(ans,f[T_T]);
if(ans==f[T_T])
idx=T_T;
dfs2(T_T,u);
}
}
signed main (void)
{
cin>>n;
for(int i=1;i<n;i++)
{
ll x,y; cin>>x>>y;
Edge[x].push_back(y);
Edge[y].push_back(x);
}
depth[0]=-1;
dfs1(1,0);
f[1]=all_depth;
ans=all_depth;
idx=1;
dfs2(1,0);
cout<<idx<<endl;
return 0;
}
示例二
题目大意:给定一个n个节点的树,每条边有一个权值,每个节点有一定数量的奶牛,找到一个点,使得所有奶牛到这个点所经过的路程最短,输出这个最短距离
解题思路:先以一为根节点进行计算,找到其他节点的子树(包含自身)所包含的奶牛数量是多少,然后进行状态转移,u为根节点,v为相邻的点,dis为u到v的距离,f[v]=f[u]+(size[1]-size[v])*dis-size[v]*dis,以v为子树的点的距离都减去dis,否则都加上dis
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PLL;
const int N=1e6+100;
#define int long long
ll n;
ll a[N];
vector<PLL> Edge[N];
ll size[N];
ll dis[N];// 算出以一为根节点
ll all_dis;
ll f[N];
ll ans;
ll idx;
ll dfs1(ll u,ll fa)
{
ll res=a[u];
all_dis+=a[u]*dis[u];
for(auto T_T:Edge[u])
{
if(T_T.first==fa)
continue;
dis[T_T.first]=dis[u]+T_T.second;
res+= dfs1(T_T.first,u);
}
return size[u]=res;
}
void dfs2(ll u,ll fa)
{
for(auto T_T:Edge[u])
{
if(fa==T_T.first)
continue;
f[T_T.first]=f[u]+(size[1]-size[T_T.first])*T_T.second-size[T_T.first]*T_T.second;
ans=min(ans,f[T_T.first]);
if(ans==f[T_T.first])
idx=T_T.first;
dfs2(T_T.first,u);
}
}
signed main (void)
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<n;i++)
{
ll x,y,z; cin>>x>>y>>z;
Edge[x].push_back({y,z});
Edge[y].push_back({x,z});
}
dfs1(1,0);
f[1]=all_dis;
ans=all_dis;
idx=1;
dfs2(1,0);
cout<<ans<<endl;
return 0;
}
示例三
题目大意:给你一个n个结点的树,每条边都有一个权值,找到一个点,找到以它为根节点,其他节点到它的最远距离最短,输出这个最短距离
解题思路:先以1这个节点为根节点,找到以这个节点为根节点,其他节点到这个节点的最远距离和次元距离f[1][1]次远,f[1][0]最远。然后进行状态转移。u为根节点,v为相邻的点,如果v是u最长路径上的点,那么v的最远距离就可以是u的次元距离加上uv之间的距离,否则就拿最长路径上的点来更新。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+100;
#define int long long
typedef pair<ll,ll> PLL;
// 怎么找到根节点和其他点之间距离的最大值呢
// 假设找到了节点u的 但是怎么转移呢
// 首先暴力搜索找到第一个点的
// 除了存这个点的最远距离 还得存这个点的次远距离
// eiei 起码差不多是吧 只不过我想全部存完
/*
对于一个节点从父亲节点推过来
如果该节点是父亲节点连上最长的一环 那么这个节点只能从父亲节点的次大值更新
最开始求的时候就必须保证最大值和次大值 不在一条链上
如果不是最长链上的一个点 那么直接从父亲节点上的最大值更新
*/
ll n;
vector<PLL> edge[N];
priority_queue<ll,vector<ll>,greater<ll>> save[N];
ll f[N];
ll ans=1e18;
// 先找到以1为根节点 所有点的最长 和次长距离
// 对的 只要找到第一个点的就好了
ll dfs_find(ll u,ll fa)
{
ll maxn=0;
for(auto [x,y]:edge[u])
{
if(x==fa)
continue;
ll len1=dfs_find(x,u)+y;
maxn=max(maxn,len1);
save[u].push(len1);
if(save[u].size()>2)
save[u].pop();
}
return f[u]=maxn;
}
void dfs_maxn(ll u,ll fa)
{
while(save[u].size()<2)
save[u].push(0);
ll first=save[u].top();
save[u].pop();
ll second=save[u].top();
save[u].pop();
ans=min(ans,second);
for(auto [x,y]:edge[u])
{
if(fa==x)
continue;
// 从根节点转移过来
if(f[x]+y==second)
save[x].push(first+y);
else
save[x].push(second+y);
while(save[x].size()>2)
save[x].pop();
dfs_maxn(x,u);
}
save[u].push(first),save[u].push(second);
}
signed main (void)
{
cin>>n;
for(int i=1;i<n;i++)
{
ll x,y,z; cin>>x>>y>>z;
edge[x].push_back({y,z});
edge[y].push_back({x,z});
}
dfs_find(1,0);
while(save[1].size()<2)
save[1].push(0);
// 然后开始进行第二遍搜索
dfs_maxn(1,0);
// for(int i=1;i<=n;i++)
// {
// cout<<i<<" ";
// while(save[i].size())
// cout<<save[i].top()<<" ",save[i].pop();
// cout<<endl;
// cout<<f[i]<<"--------------"<<endl;
// }
cout<<ans<<endl;
return 0;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PLL;
const int N=1e6+100;
#define int long long
ll n;
vector<PLL> Edge[N];
ll f[N][2];
ll ans=1e18;
void dfs1(ll u,ll fa)
{
for(auto T_T:Edge[u])
{
if(T_T.first==fa)
continue;
dfs1(T_T.first,u);
// 用孩子节点的距离更新当前点的最远和次元距离
if(f[T_T.first][0]+T_T.second>f[u][0])
f[u][1]=f[u][0],f[u][0]=f[T_T.first][0]+T_T.second;
else if(f[T_T.first][0]+T_T.second>f[u][1])
f[u][1]=f[T_T.first][0]+T_T.second;
}
}
// len 以u为根节点时除了自身子树之外的最大深度
void dfs2(ll u,ll fa,ll len)
{
ans=min(ans,max(len,f[u][0]));
for(auto T_T:Edge[u])
{
if(fa==T_T.first)
continue;
// 换根
if(f[T_T.first][0]+T_T.second==f[u][0]) {
dfs2(T_T.first,u,max(f[u][1],len)+T_T.second);
}
else
dfs2(T_T.first,u,max(f[u][0],len)+T_T.second);
}
}
signed main (void)
{
cin>>n;
for(int i=1;i<n;i++)
{
ll x,y,z; cin>>x>>y>>z;
Edge[x].push_back({y,z});
Edge[y].push_back({x,z});
}
dfs1(1,0);
dfs2(1,0,0);
cout<<ans<<endl;
return 0;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PLL;
const int N=1e6+100;
#define int long long
ll n;
vector<PLL> Edge[N];
ll f[N][2];
ll ans[N];
void dfs1(ll u,ll fa)
{
for(auto T_T:Edge[u])
{
if(T_T.first==fa)
continue;
dfs1(T_T.first,u);
// 用孩子节点的距离更新当前点的最远和次元距离
if(f[T_T.first][0]+T_T.second>f[u][0])
f[u][1]=f[u][0],f[u][0]=f[T_T.first][0]+T_T.second;
else if(f[T_T.first][0]+T_T.second>f[u][1])
f[u][1]=f[T_T.first][0]+T_T.second;
}
}
// len 以u为根节点时除了自身子树之外的最大深度
void dfs2(ll u,ll fa,ll len)
{
ans[u]=max(len,f[u][0]);
for(auto T_T:Edge[u])
{
if(fa==T_T.first)
continue;
// 换根
if(f[T_T.first][0]+T_T.second==f[u][0]) {
dfs2(T_T.first,u,max(f[u][1],len)+T_T.second);
}
else
dfs2(T_T.first,u,max(f[u][0],len)+T_T.second);
}
}
signed main (void)
{
cin>>n;
for(int i=2;i<=n;i++)
{
ll x,y; cin>>x>>y;
Edge[x].push_back({i,y});
Edge[i].push_back({x,y});
}
dfs1(1,0);
dfs2(1,0,0);
for(int i=1;i<=n;i++)
cout<<ans[i]<<endl;
return 0;
}
示例四
题目大意:给你一个n个结点的树,最初是以一号节点为根,所获得的收益是其他节点到根节点的最远距离,每次换根需要耗费c元,询问你最大利益是多少
解题思路:与示例三相同,不过需要在第二次dfs的时候记录一下当前是第几次换根
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PLL;
const int N=1e6+100;
#define int long long
ll t;
ll n,k,c;
ll f[N][2];
ll ans;
signed main (void)
{
cin>>t;
while(t--) {
ans = -1e18;
cin >> n >> k >> c;
vector<vector<PLL>> Edge(n + 10);
vector<vector<ll>> f(n+10, vector<ll>(2, 0));
for (int i = 2; i <= n; i++) {
ll x, y;
cin >> x >> y;
Edge[x].push_back({y, k});
Edge[y].push_back({x, k});
}
function<void(int, int)> dfs1 = [&](ll u, ll fa) {
for (auto T_T: Edge[u]) {
if (T_T.first == fa)
continue;
dfs1(T_T.first, u);
// 用孩子节点的距离更新当前点的最远和次元距离
if (f[T_T.first][0] + T_T.second > f[u][0])
f[u][1] = f[u][0], f[u][0] = f[T_T.first][0] + T_T.second;
else if (f[T_T.first][0] + T_T.second > f[u][1])
f[u][1] = f[T_T.first][0] + T_T.second;
}
};
function<void(int, int, int, int)> dfs2 = [&](ll u, ll fa, ll len, ll cnt) {
ans = max(ans, max(f[u][0], len) - cnt * c);
for (auto T_T: Edge[u]) {
if (fa == T_T.first)
continue;
// 换根
if (f[T_T.first][0] + T_T.second == f[u][0]) {
dfs2(T_T.first, u, max(f[u][1], len) + T_T.second, cnt + 1);
} else
dfs2(T_T.first, u, max(f[u][0], len) + T_T.second, cnt + 1);
}
};
dfs1(1, 0);
dfs2(1, 0, 0, 0);
cout << ans << endl;
}
return 0;
}
示例五
题目大意:给你一个n个节点的树,每条边都有一个权值,询问有多少个i,j(i<j)的距离是2019的倍数
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PLL;
const int N=1e6+100;
#define int long long
ll t;
ll n;
ll ans;
signed main (void)
{
while(cin>>n) {
ans = 0;
vector<vector<PLL>> Edge(n + 10);
vector<vector<ll>> dis(n+10, vector<ll>(3000, 0));
//以第i个点为根节点 第i个点子树 它的路径长度为j的方案数是多少
vector<ll> num(3000,0);
for (int i = 1; i < n; i++) {
ll x, y,z;
cin >> x >> y>>z;
Edge[x].push_back({y, z});
Edge[y].push_back({x, z});
}
function<void(int, int)> dfs1 = [&](ll u, ll fa) {
for (auto T_T: Edge[u]) {
if (T_T.first == fa)
continue;
dfs1(T_T.first, u);
dis[u][T_T.second]+=1;
// 用孩子节点的距离更新当前点的最远和次元距离
for(int i=0;i<2019;i++)
dis[u][(i+T_T.second)%2019]+=dis[T_T.first][i];
}
};
function<void(int, int)> dfs2 = [&](ll u, ll fa) {
ans+=dis[u][0];
for (auto T_T: Edge[u]) {
if (fa == T_T.first)
continue;
// 换根
// 先算算那一部分的路径是应该减去T_T.second 那一部分的路径是加上T_T.second
for(int i=0;i<2019;i++)
num[(i+T_T.second)%2019]=dis[u][(i+T_T.second)%2019]-dis[T_T.first][i];
// num这一部分就是加上2019的部分
num[T_T.second]--;
dis[T_T.first][T_T.second]++;
for(int i=0;i<2019;i++)
dis[T_T.first][(i+T_T.second)%2019]+=num[i];
dfs2(T_T.first,u);
}
};
dfs1(1, 0);
dfs2(1, 0);
cout << ans/2 << endl;
}
return 0;
}
示例六
题目大意:有一颗n个节点的树,每一个节点都有一个ci的权值,假定以i为根节点,那么需要花费每个节点的c乘上每个节点到i节点的节点数的和,询问最小的花费是多少
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PLL;
const int N=1e6+100;
#define int long long
ll n;
vector<ll> Edge[N];
ll f[N];
ll all_depth;
ll ans;
ll val[N];
ll a[N];
// 咱就是说这一步求的应该是什么呢
void dfs1(ll u,ll fa,ll depth)
{
f[1]+=depth*a[u];
val[u]=a[u];
for(auto T_T:Edge[u])
{
if(T_T==fa)
continue;
dfs1(T_T,u,depth+1);
val[u]+=val[T_T];
}
}
void dfs2(ll u,ll fa)
{
for(auto T_T:Edge[u])
{
if(fa==T_T)
continue;
f[T_T]=f[u]-val[T_T]*2+val[1];
ans=min(ans,f[T_T]);
dfs2(T_T,u);
}
}
signed main (void)
{
cin>>n;
for(int i=1;i<n;i++)
{
ll x,y; cin>>x>>y;
Edge[x].push_back(y);
Edge[y].push_back(x);
}
for(int i=1;i<=n;i++)
cin>>a[i];
dfs1(1,0,0);
ans=f[1];
dfs2(1,0);
cout<<ans<<endl;
return 0;
}