树形 d p dp dp问题主要有如下两种:
第一种:简单的单向dp即可求出答案,比如从根部向下递归做dp或者从叶子往上做dp
第二种:双向dp即换根dp,需要做两次dfs一次向下一次向上
下面直接上例题
You are given a tree consisting exactly of n vertices. Tree is a connected undirected graph with n−1 edges. Each vertex v of this tree has a value av assigned to it.
Let dist(x,y) be the distance between the vertices x and y. The distance between the vertices is the number of edges on the simple path between them.
Let’s define the cost of the tree as the following value: firstly, let’s fix some vertex of the tree. Let it be v. Then the cost of the tree is ∑ i = 1 n d i s t ( i , v ) ∗ a i \sum_{i=1}^{n}{dist(i,v)*a_i} i=1∑ndist(i,v)∗ai
Your task is to calculate the maximum possible cost of the tree if you can choose v arbitrarily.
Input
The first line contains one integer n, the number of vertices in the tree ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) (1≤n≤2⋅10^5) (1≤n≤2⋅105).
The second line of the input contains n integers a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an ( 1 ≤ a i ≤ 2 ⋅ 1 0 5 ) (1≤a_i≤2⋅10^5) (1≤ai≤2⋅105), where ai is the value of the vertex i i i.
Each of the next n−1 lines describes an edge of the tree. Edge i is denoted by two integers ui and vi, the labels of vertices it connects ( 1 ≤ u i , v i ≤ n , u i ! = v i 1≤u_i,v_i≤n, u_i\ !=v_i 1≤ui,vi≤n,ui !=vi).
It is guaranteed that the given edges form a tree.
Print one integer — the maximum possible cost of the tree if you can choose any vertex as v.
input
8
9 4 1 7 10 1 6 5
1 2
2 3
1 4
1 5
5 6
5 7
5 8
output
121
input
1
1337
output
0
Picture corresponding to the first example:
You can choose the vertex 3 as a root, then the answer will be 2⋅9+1⋅4+0⋅1+3⋅7+3⋅10+4⋅1+4⋅6+4⋅5=18+4+0+21+30+4+24+20=121.
In the second example tree consists only of one vertex so the answer is always 0.
#include
using namespace std;
typedef long long ll;
const int maxn = 200005;
int n, u, v, a[maxn], fath[maxn];
vector<int> vec[maxn];
ll sum[maxn], s[maxn];
ll dp[maxn];
ll solve(int cur, int fa)
{
dp[cur] = dp[fath[cur]] + sum[1] - 2 * sum[cur];
ll res = dp[cur];
for(int i = 0; i < vec[cur].size(); i++)
{
if(vec[cur][i] != fa)
{
res = max(res, solve(vec[cur][i], cur));
}
}
return res;
}
void dfs(int cur, int fa)
{
sum[cur] = a[cur];
fath[cur] = fa;
for(int i = 0; i < vec[cur].size(); i++)
{
if(vec[cur][i] != fa)
{
dfs(vec[cur][i], cur);
sum[cur] += sum[vec[cur][i]];
s[cur] += s[vec[cur][i]] + sum[vec[cur][i]];
}
}
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i < n; i++)
{
scanf("%d %d", &u, &v);
vec[u].push_back(v);
vec[v].push_back(u);
}
dfs(1, 0);
dp[0] = s[1] + sum[1];
printf("%lld\n", solve(1, 0));
}
鸽克多正在为他的国家规划一张设计图,在设计图上有n个城市(按1,2,3,··,,n标号),城市间共有n - 1条道路,使得任意两个城市间都有路径可以互达。但不幸的是,每条道路的施工方可能会放鸽子,即在实际施工结束后,每条道路有0.5的概率无法投入使用,导致可能存在一些城市没有路径可以互达定义。每座城市的交通指数为从该城市出发,分别到其他可达的城市的最短路径长度之和(路径长度为经过的道路数量)。对于给出的一张设计图,鸽克多想知道按该设计图施工后所有城市的交通指数之和的期望。
有多组输入数据,第一行一个整数T表示数据组数,对于每组数据,有n行,每组数据第一行一个整数n表示城市数,接下来n − 1行,每行两个整数u(1 ⩽ u ⩽ n),v(1 ⩽ v ⩽ n),表示在该设计图上城市u和城市v之间有一条道路。1 ⩽ n ⩽ 1 0 5 10^5 105。保证对于所有数据 ∑ n \sum{n} ∑n ⩽ 2 × 1 0 6 10^6 106。
对于每组输入数据,输出一行一个整数,表示按该设计图施工后所有城市的交通指数之和的期望,注意答案可以表示为一个分数 P Q \frac{P}{Q} QP, P 、 Q P、Q P、Q互质,且 Q ≠ 0 m o d Q \neq 0 mod Q=0mod ( 1 0 9 + 7 ) (10^9 + 7) (109+7),你只需要输出 P × Q − 1 m o d ( 1 0 9 + 7 ) P × Q^{-1} mod (10^{9} + 7) P×Q−1mod(109+7)。( Q − 1 Q^{−1} Q−1即 Q Q Q对模 1 0 9 + 7 10^9 + 7 109+7的逆元,即 Q × Q − 1 m o d ( 1 0 9 + 7 ) = 1 Q × Q^{-1} mod (10^9 + 7) = 1 Q×Q−1mod(109+7)=1。
4
1
2
1 2
3
1 2
2 3
5
1 2
1 3
2 4
2 5
0
1
3
500000013
输入文件较大,不建议使用cin, cout,推荐使用 scanf, printf
给出几个数组的定义:
s [ c u r ] = ∑ i d i s ( i , c u r ) × ( 1 2 ) d i s ( i , c u r ) s[cur]=\sum_{}^{i}{dis(i,cur) \times(\frac{1}{2})^{dis(i,cur)}} s[cur]=∑idis(i,cur)×(21)dis(i,cur) s u m [ c u r ] = ∑ i ( 1 2 ) d i s ( i , c u r ) sum[cur]=\sum_{}^{i}{(\frac{1}{2})^{dis(i,cur)}} sum[cur]=∑i(21)dis(i,cur)
其中 i i i是 c u r cur cur的子孙节点,即不包括 c u r cur cur
d p _ s [ c u r ] = ∑ i d i s ( i , c u r ) × ( 1 2 ) d i s ( i , c u r ) dp\_s[cur]=\sum_{}^{i}{dis(i,cur) \times(\frac{1}{2})^{dis(i,cur)}} dp_s[cur]=∑idis(i,cur)×(21)dis(i,cur) d p _ s u m [ c u r ] = ∑ i ( 1 2 ) d i s ( i , c u r ) dp\_sum[cur]=\sum_{}^{i}{(\frac{1}{2})^{dis(i,cur)}} dp_sum[cur]=∑i(21)dis(i,cur)
其中 i i i表示除去以 c u r cur cur为根的子树中的节点的所有节点
类似上面的思想,读者可以自己手推一下,这里直接给出递推方程:
s u m [ c u r ] = ∑ ( 1 2 s u m [ s o n i ] + 1 2 ) sum[cur]=\sum_{}^{}{(\frac{1}{2}sum[son_i]+\frac{1}{2})} sum[cur]=∑(21sum[soni]+21) s [ c u r ] = s u m [ c u r ] + ∑ 1 2 s [ s o n i ] s[cur]=sum[cur]+\sum_{}^{}{\frac{1}{2}s[son_i]} s[cur]=sum[cur]+∑21s[soni] d p _ s [ c u r ] = 1 2 ( d p _ s [ f a [ c u r ] ] + d p _ s u m [ f a [ c u r ] ] + 1 ) + 1 2 ( s [ f a [ c u r ] ] dp\_s[cur]=\frac{1}{2}(dp\_s[fa[cur]]+dp\_sum[fa[cur]]+1)+\frac{1}{2}(s[fa[cur]] dp_s[cur]=21(dp_s[fa[cur]]+dp_sum[fa[cur]]+1)+21(s[fa[cur]] − 1 2 ( s [ c u r ] + s u m [ c u r ] + 1 ) ) + 1 2 ( s u m [ f a [ c u r ] ] − 1 2 s u m [ c u r ] − 1 2 ) -\frac{1}{2}(s[cur]+sum[cur]+1))+\frac{1}{2}(sum[fa[cur]]-\frac{1}{2}sum[cur]-\frac{1}{2}) −21(s[cur]+sum[cur]+1))+21(sum[fa[cur]]−21sum[cur]−21) d p _ s u m [ c u r ] = 1 2 d p _ s u m [ f a [ c u r ] ] + 1 2 + 1 2 ( s u m [ f a [ c u r ] ] − 1 2 s u m [ c u r ] − 1 2 ) dp\_sum[cur]=\frac{1}{2}dp\_sum[fa[cur]]+\frac{1}{2}+\frac{1}{2}(sum[fa[cur]]-\frac{1}{2}sum[cur]-\frac{1}{2}) dp_sum[cur]=21dp_sum[fa[cur]]+21+21(sum[fa[cur]]−21sum[cur]−21)
显然节点 c u r cur cur的结果为 d p _ s [ c u r ] + s [ c u r ] dp\_s[cur]+s[cur] dp_s[cur]+s[cur],最后将所有的相加即可,复杂度 O ( n ) O(n) O(n)
#include
using namespace std;
typedef long long ll;
const int maxn = 100005;
const ll mod = 1e9 + 7;
int t, n, u, v, fath[maxn];
vector<int> vec[maxn];
ll sum[maxn], s[maxn], dp_s[maxn], dp_sum[maxn], inv2;
ll quick_pow(ll a, ll b)
{
ll res = 1ll;
while(b)
{
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll solve(int cur, int fa)
{
ll up = fa == 0 ? 0ll : ((dp_s[fath[cur]] + dp_sum[fath[cur]] + 1ll) % mod) * inv2 % mod;
ll down = (((s[fath[cur]] - ((s[cur] + sum[cur] + 1) * inv2 % mod)) % mod + mod) % mod) * inv2 % mod;
ll rest = (((sum[fath[cur]] - (inv2 * sum[cur] % mod) - inv2) % mod + mod) % mod) * inv2 % mod;
dp_s[cur] = (up + down + rest) % mod;
dp_sum[cur] = fa == 0 ? 0ll : ((dp_sum[fath[cur]] * inv2 % mod + inv2 + ((sum[fath[cur]] - (sum[cur] * inv2 % mod) - inv2) * inv2 % mod)) % mod + mod) % mod;
ll res = (dp_s[cur] + s[cur]) % mod;
for(int i = 0; i < vec[cur].size(); i++)
{
if(vec[cur][i] != fa)
{
res = (res + solve(vec[cur][i], cur)) % mod;
}
}
return res;
}
void dfs(int cur, int fa)
{
fath[cur] = fa;
for(int i = 0; i < vec[cur].size(); i++)
{
if(vec[cur][i] != fa)
{
dfs(vec[cur][i], cur);
sum[cur] = (sum[cur] + (sum[vec[cur][i]] * inv2 % mod) + inv2) % mod;
s[cur] = (s[cur] + ((inv2 * s[vec[cur][i]]) % mod)) % mod;
}
}
s[cur] = (s[cur] + sum[cur]) % mod;
}
void init()
{
for(int i = 1; i <= n; i++) vec[i].clear();
memset(sum, 0, sizeof(sum));
memset(s, 0, sizeof(s));
memset(dp_s, 0, sizeof(dp_s));
memset(dp_sum, 0, sizeof(dp_sum));
}
int main()
{
scanf("%d", &t);
inv2 = quick_pow(2ll, mod - 2);
while(t--)
{
scanf("%d", &n);
init();
for(int i = 1; i < n; i++)
{
scanf("%d %d", &u, &v);
vec[u].emplace_back(v);
vec[v].emplace_back(u);
}
dfs(1, 0);
s[0] = (s[1] + sum[1] + 1ll) * inv2 % mod;
sum[0] = (sum[1] * inv2 % mod + inv2) % mod;
printf("%lld\n", solve(1, 0));
}
}
Let’s call an ordered pair of vertices (x,y) (x≠y) valid if, while traversing the simple path from x to y, we never go through a 0-edge after going through a 1-edge. Your task is to calculate the number of valid pairs in the tree.
The first line contains one integer n (2 ≤ ≤ ≤ n ≤ ≤ ≤ 200000) — the number of vertices in the tree.
Then n−1 lines follow, each denoting an edge of the tree. Each edge is represented by three integers x i , y i x_i, y_i xi,yi and c i c_i ci ( 1 ≤ x i , y i ≤ n , 0 ≤ c i ≤ 1 , x i ! = y i ) (1 ≤ x_i,y_i≤ n, 0 ≤ c_i ≤ 1, x_i\ !=y_i) (1≤xi,yi≤n,0≤ci≤1,xi !=yi) — the vertices connected by this edge and the number written on it, respectively.
It is guaranteed that the given edges form a tree.
Print one integer — the number of valid pairs of vertices.
input
7
2 1 1
3 2 0
4 2 1
5 2 0
6 7 1
7 2 1
output
34
The picture corresponding to the first example:
#include
using namespace std;
const int maxn = 200005;
typedef long long ll;
struct node
{
int to, val;
};
int n, u, v, w;
vector<node> vec[maxn];
int dp[maxn][2];
ll solve(int cur, int fa, int v)
{
ll res = dp[cur][0] + dp[cur][1], sum0 = 0, sum1 = 0;
for(int i = 0; i < vec[cur].size(); i++){
auto sun = vec[cur][i];
if(sun.to != fa){
if(sun.val) sum1 += dp[sun.to][0] + 1;
else{
sum0+=dp[sun.to][0]+1;
for(int j = 0; j < vec[sun.to].size(); j++){
auto son = vec[sun.to][j];
if(son.to != cur && son.val) sum0 -= dp[son.to][0] + 1;
}
}
}
}
for(int i = 0; i < vec[cur].size(); i++){
auto sun = vec[cur][i];
if(sun.to != fa){
if(sun.val) dp[sun.to][1] = sum0 + sum1 - (dp[sun.to][0] + 1) + dp[cur][1] + 1;
else{
if(!v) dp[sun.to][1] += dp[cur][1];
ll tot=dp[sun.to][0]+1;
for(int j = 0; j < vec[sun.to].size(); j++){
auto son = vec[sun.to][j];
if(son.to != cur && son.val) tot -= dp[son.to][0] + 1;
}
dp[sun.to][1] += sum0 - tot + 1;
}
res += solve(sun.to, cur, sun.val);
}
}
return res;
}
void dfs(int cur, int fa)
{
for(int i = 0; i < vec[cur].size(); i++){
if(vec[cur][i].to != fa){
dfs(vec[cur][i].to, cur);
}
}
for(int i = 0; i < vec[cur].size(); i++){
auto sun = vec[cur][i];
if(sun.to != fa){
if(sun.val) dp[cur][0] += dp[sun.to][0] + 1;
else{
dp[cur][0] += dp[sun.to][0]+1;
for(int j = 0; j < vec[sun.to].size(); j++){
auto grand = vec[sun.to][j];
if(grand.to != cur && grand.val) dp[cur][0] -= dp[grand.to][0] + 1;
}
}
}
}
}
int main()
{
scanf("%d", &n);
for(int i = 1; i < n; i++){
scanf("%d %d %d", &u, &v, &w);
vec[u].push_back(node{v, w});
vec[v].push_back(node{u, w});
}
dfs(1, 0);
printf("%lld\n", solve(1, 0, 0));
}
The Fair Nut is going to travel to the Tree Country, in which there are ? cities. Most of the land of this country is covered by forest. Furthermore, the local road system forms a tree (connected graph without cycles). Nut wants to rent a car in the city ? and go by a simple path to city ?. He hasn’t determined the path, so it’s time to do it. Note that chosen path can consist of only one vertex.
A filling station is located in every city. Because of strange law, Nut can buy only ?? liters of gasoline in the ?-th city. We can assume, that he has infinite money. Each road has a length, and as soon as Nut drives through this road, the amount of gasoline decreases by length. Of course, Nut can’t choose a path, which consists of roads, where he runs out of gasoline. He can buy gasoline in every visited city, even in the first and the last.
He also wants to find the maximum amount of gasoline that he can have at the end of the path. Help him: count it.
The first line contains a single integer n ( 1 ≤ n ≤ 3 ⋅ 1 0 5 ) n (1≤n≤3⋅10^5) n(1≤n≤3⋅105) — the number of cities.
The second line contains ? integers w 1 , w 2 , … , w n ( 0 ≤ w i ≤ 109 ) w_1,w_2,…,w_n (0≤w_i≤109) w1,w2,…,wn(0≤wi≤109) — the maximum amounts of liters of gasoline that Nut can buy in cities.
Each of the next ?−1 lines describes road and contains three integers u , v , v ( 1 ≤ u , v ≤ n , 1 ≤ c ≤ 1 0 9 , u ! = v ) u, v, v (1≤u,v≤n, 1≤c≤10^9, u\ !=v) u,v,v(1≤u,v≤n,1≤c≤109,u !=v), where ? and ? — cities that are connected by this road and ? — its length.
It is guaranteed that graph of road connectivity is a tree.
Print one number — the maximum amount of gasoline that he can have at the end of the path.
3
1 3 3
1 2 2
1 3 2
3
5
6 3 2 5 0
1 2 10
2 3 3
2 4 1
1 5 1
7
The optimal way in the first example is 2→1→3.
The optimal way in the second example is 2→4.
#include
using namespace std;
typedef long long ll;
const int maxn=300005;
struct node{
int pos,cost;
node(int a=0,int b=0){
pos=a;cost=b;
}
};
vector<node> vec[maxn];
int val[maxn],n,u,v,w;
ll dp[2][maxn];
set<pair<ll,int> >s;
void dfs(int cur,int fa)
{
dp[0][cur]=val[cur];
for(int i=0;i<vec[cur].size();i++){
auto nxt=vec[cur][i];
if(nxt.pos!=fa){
dfs(nxt.pos,cur);
dp[0][cur]=max(dp[0][cur],dp[0][nxt.pos]+1LL*val[cur]-nxt.cost);
}
}
}
ll solve(int cur,int fa)
{
ll res=(dp[0][cur]+dp[1][cur]);
for(int i=0;i<vec[cur].size();i++){
auto nxt=vec[cur][i];
if(nxt.pos!=fa){
s.insert(make_pair(dp[0][nxt.pos]-nxt.cost+val[cur],nxt.pos));
}
}
for(int i=0;i<vec[cur].size();i++){
auto nxt=vec[cur][i];
if(nxt.pos!=fa){
s.erase(make_pair(dp[0][nxt.pos]-nxt.cost+val[cur],nxt.pos));
if(!s.empty()){
auto maxx=*s.rbegin();
dp[1][nxt.pos]=max(dp[1][nxt.pos],max(maxx.first-nxt.cost,1LL*val[cur]-nxt.cost));
}else dp[1][nxt.pos]=max(dp[1][nxt.pos],1LL*val[cur]-nxt.cost);
s.insert(make_pair(dp[0][nxt.pos]-nxt.cost+val[cur],nxt.pos));
}
}
s.clear();
for(int i=0;i<vec[cur].size();i++){
auto nxt=vec[cur][i];
if(nxt.pos!=fa){
res=max(res,solve(nxt.pos,cur));
}
}
return res;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&val[i]);
for(int i=1;i<n;i++){
scanf("%d %d %d",&u,&v,&w);
vec[u].push_back(node(v,w));
vec[v].push_back(node(u,w));
}
dfs(1,0);
printf("%lld\n",solve(1,0));
}