Codeforces 1088E 树形dp+思维

比赛的时候看到题意没多想就放弃了。结果最后D也没做出来,还掉分了,所以还是题目做的太少,人太菜。

回到正题:

题意:一棵树,点带权值,然后求k个子连通块,使得k个连通块内所有的点权值相加作为分子除以k的值最大,如果这样的最大值有多个,就最大化k。

赛后看了看别人的代码仔细想了一想,还是挺容易的。

首先将树分为若干个连通块,考虑一个权值求和最大的连通块,设该最大值为sum,那么其他所有的连通块,权值求和都不可能比当前的连通块大。

在不考虑最大化k的情况下,就没有必要再将其他的连通块加进来(因为如果加进来,(sum+加进来的连通块权值和)/(k+1)<=sum/k,就必定成立,其实本质上就是求一个均值,加比当前值还要小的值会使得这个均值变小),所以答案就是sum。

现在要考虑答案相同时,最大化k,那么本质上只要将其他的权值和等同于当前最大权值和的连通块加进来就好,这样分子就变成了sum*k,k个连通块。

经队友赛后一提醒,其实实际上这就是一个最大连续和的树上版本,树形dp一下就出来了。

设dp[i]表示以i为根的子树,包含i在内的权值求和最大的连通块的权值和。

状态转移就有两种选择,枚举他的子树,那么:1.连这棵子树;2.不连这颗子树。

易得状态转移方程:

                    dp[i]+=max(dp[j],0) (j为i的子节点)(类似于线性结构上的最大连续和)

然后获取所有dp中的最大值,就是sum。

最后再遍历一遍dp数组,每有一个dp值与sum相同,就k++,最后答案就是sum*k,k个连通块。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 const int maxn=300005;
 5 int cnt;
 6 int head[maxn];
 7 struct edge
 8 {
 9     int to,nxt;
10 }e[maxn];
11 ll a[maxn];
12 void inline addedge(int u,int v)
13 {
14     e[++cnt].to=v;
15     e[cnt].nxt=head[u];
16     head[u]=cnt;
17 }
18 ll dp[maxn];
19 ll ans=-1e18;
20 void dfs(int fa,int u)
21 {
22     dp[u]=a[u];
23     for(int i=head[u];i;i=e[i].nxt)
24     {
25         int v=e[i].to;
26         if(v==fa) continue;
27         dfs(u,v);
28         dp[u]+=max(dp[v],0LL);
29     }
30     ans=max(ans,dp[u]);
31 }
32 int main()
33 {
34     #ifdef local
35         //freopen("in.txt","r",stdin);
36     #endif // local
37     int n;
38     cin>>n;
39     for(int i=1;i<=n;i++)
40         cin>>a[i];
41     for(int i=1;i<=n-1;i++)
42     {
43         int x,y;
44         cin>>x>>y;
45         addedge(x,y);
46         addedge(y,x);
47     }
48     dfs(-1,1);
49     ll k=0;
50     for(int i=1;i<=n;i++)
51         if(dp[i]==ans)
52             k++;
53     cout<<ans*k<<" "<<k<<endl;
54 }

原文地址:https://www.cnblogs.com/iamamori/p/10068831.html

时间: 2024-10-01 00:31:51

Codeforces 1088E 树形dp+思维的相关文章

Codeforces 77C 树形dp + 贪心

题目链接:点击打开链接 题意: 给定n个点, 每个点的豆子数量 下面是一棵树 再给出起点 每走到一个点,就会把那个点的豆子吃掉一颗. 问:回到起点最多能吃掉多少颗豆子 思路:树形dp 对于当前节点u,先把子节点v都走一次. 然后再往返于(u,v) 之间,直到u点没有豆子或者v点没有豆子. dp[u] 表示u点的最大值.a[u] 是u点剩下的豆子数. #include <cstdio> #include <vector> #include <algorithm> #inc

CodeForces 274B 树形dp

//继续水一道树形dp 1 #include "iostream" 2 #include "cstdio" 3 #include "cstring" 4 #include "algorithm" 5 #include "cmath" 6 using namespace std; 7 __int64 dp[100010][2]; 8 bool vis[100010]; 9 int tot, first[100

codeforces 219D 树形dp

题目大意: 根据n个城市,设置n-1条有向边,希望设定一个中心城市,能通过修改一些道路的方向到达任何一座城市,希望修改的道路数量最少 输出这个最少的道路数量,并且把所有符合的可作为中心的城市编号一个个输出来 开始也实在想不出,根据树形dp,首先要确定一棵树,可是这里的边乱七八糟的,没法确定根 后来看了别人的思路,原来还是自己太死脑筋了,根本不需要确定根,可以把任意一个城市作为根来解决题目,这里假定1为根 把所有的有向边看作无向边,但要记录那条边是真实存在的,哪条是自己加的 用dp[i]表示 i

Codeforces 743D 树形dp

D. Chloe and pleasant prizes 题意:一棵树,以结点1为根结点悬挂在墙上,每个点有一个权值.选两条边切断,要求:刚好掉下两份结点,且两份不能属于同一子树.求两份结点可能的最大权值和. tags:裸的树dp. #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for

codeforces 500d 树形dp和概率

求期望,公式是从树中任选3个点,共有C(n, 3)种情况,所有情况的和 / C(n, 3)即为答案. 对于树中的任意两个点,可以构成一条固定的路径,再从剩下的n - 2个点找一个点,可以得到另外两条路径,共有n - 2种情况. 所以每条路径出现的次数为n - 2次. 假设边Ei共有Ci条路径经过,则对于i = 1 : n, Wi * Ci的和即为所有路径的权和, 结果为所有路径的权和 * (n - 2) / C(n, 3); 化简得到公式:所有路径的权和 / n / (n - 1) * 6. #

CodeForces 109C 树形DP Lucky Tree

赶脚官方题解写得挺清楚的说,=_= 注意数据范围用long long,否则会溢出. 1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <vector> 6 using namespace std; 7 8 const int maxn = 100000 + 10; 9 10 int n; 11 vecto

CodeForces 519E 树形DP A and B and Lecture Rooms

给出一棵树,有若干次询问,每次询问距两个点u, v距离相等的点的个数. 情况还挺多的,少侠不妨去看官方题解.^_^ 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 using namespace std; 7 8 const int maxn = 100000 + 10; 9 10

Codeforces 1065F(树形dp)

题目链接 题意:给一棵树,进行如下操作,如果当前点非叶子,则往子树移动,否则最多向上移动k次,问从根节点开始最多访问多少叶子 思路:预处理出每个点最多能“白嫖”到几个叶子,根据下一个点的状态更新最优方案 #include <bits/stdc++.h> #define DBG(x) cerr << #x << " = " << x << endl; const int maxn = 1e6+5; const int maxm

CF482D Random Function and Tree 树形DP + 思维 + 神题

Code: #include<bits/stdc++.h> #define ull unsigned long long #define MOD 1000000007 #define ll long long #define maxn 120000 using namespace std; void setIO(string s) { string in=s+".in"; string out=s+".out"; freopen(in.c_str(),&