Manthan, Codefest 16 F

寻找树上最大权值和的两条不相交的路径。

树形DP题。挺难的,对于我……

定义三个变量ma[MAXN], t[MAXN], sum[MAXN]

其中,ma[i]代表i子树中,最长的路径和

t[i]代表i子树中,用来维护已有一条路径,而且还有一条链从叶子节点到i,则可以从根节点i向上扩展。如下图,维护红色部分

sum[i]维护从某叶子节点到根节点i的最长路径。

转移方程可以看代码,很容易明白

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <set>
#define LL long long

using namespace std;

const int MAXN = 100050;
const int MOD = 1e9 + 7;

LL ans;
LL ma[MAXN], t[MAXN], sum[MAXN];
LL l[MAXN], ml[MAXN], r[MAXN], mr[MAXN];
bool leaf[MAXN];
LL mm[10];
bool vis[MAXN];
int head[MAXN];
struct Edge{
	int u, v;
	int next;
}edge[MAXN * 2];
int weight[MAXN], par[MAXN];
int n, tot;

void addedge(int u, int v){
	edge[tot].u = u;
	edge[tot].v = v;
	edge[tot].next = head[u];
	head[u] = tot++;
}

void dfs(int u){
	vis[u] = true;
	leaf[u] = true;
	for(int e = head[u]; e != -1; e = edge[e].next){
		int v = edge[e].v;
		if(vis[v]) continue;
		par[v] = u;
		dfs(v);
		leaf[u] = false;
	}
}

void slove(int u){
	if(leaf[u]){
		ma[u] = t[u] = sum[u] = weight[u];
		return ;
	}

	LL m1, m2, M1, M2;
	m1 = m2 = M1 = M2 = 0;
	for(int e = head[u]; e != -1; e = edge[e].next){
		int v = edge[e].v;
		if(v != par[u]){
			slove(v);
			if(ma[v] >= M1){
				M2 = M1, M1 = ma[v];
			}
			else if(ma[v] > M2){
				M2 = ma[v];
			}
			if(sum[v] >= m1){
				m2 = m1, m1 = sum[v];
			}
			else if(sum[v] > m2){
				m2 = sum[v];
			}
			t[u] = max(t[u], t[v] + weight[u]);
		}
	}
	ma[u] = max(M1, m1 + m2 + weight[u]);
	sum[u] = m1 + weight[u];
	ans = max(ans, M1 + M2);

	int counts = 0;
	for(int e = head[u]; e != -1; e = edge[e].next){
		int v = edge[e].v;
		if(v != par[u]){
			l[++counts] = sum[v];
			r[counts] = sum[v];
		}
	}
	l[0] = ml[0] = r[counts + 1] = mr[counts + 1] = 0;

	//从左往右寻找最大的两个sum

	for(int i = 1; i <= counts ; i++){
		if(l[i] > l[i - 1]) ml[i] = l[i - 1];
		else if(l[i] > ml[i - 1]){
			ml[i] = l[i];
			l[i] = l[i - 1];
		}
		else{
			l[i] = l[i - 1], ml[i] = ml[i - 1];
		}
	}

	//从右往左。。。。

	for(int i = counts; i >= 1; i--){
		if(r[i] > r[i + 1]) mr[i] = r[i + 1];
		else if(r[i] > mr[i + 1]){
			mr[i] = r[i];
			r[i] = r[i + 1];
		}
		else{
			r[i] = r[i + 1], mr[i] = mr[i + 1];
		}
	}

	counts = 0;
	for(int e = head[u]; e != -1; e = edge[e].next){
		int v = edge[e].v;
		if(v == par[u]) continue;
		counts ++;
		mm[0] = l[counts - 1], mm[1] = ml[counts - 1];
		mm[2] = r[counts + 1], mm[3] = mr[counts + 1];

		sort(mm, mm + 4);

		ans = max(ans, weight[u] + ma[v] + mm[3] + mm[2]);
		ans = max(ans, weight[u] + mm[3] + t[v]);
		t[u] = max(t[u], ma[v] + weight[u] + mm[3]);
	}

}

int main(){
	scanf("%d", &n);
	memset(head, -1, sizeof(head));
//	memset(vis, false, sizeof(vis));
//	memset(leaf, false, sizeof(leaf));
	tot = 0;
	memset(t, 0, sizeof(t));
	for(int i = 1; i <= n; i++){
		scanf("%d", &weight[i]);
	}
	int u, v;
	memset(par, -1, sizeof(par));
	for(int i = 0; i < n - 1; i++){
		scanf("%d%d", &u, &v);
		addedge(u, v);
		addedge(v, u);
	}
	dfs(1);
	ans = 0;
	slove(1);

	cout << ans << endl;
}

  

时间: 2024-10-24 12:27:56

Manthan, Codefest 16 F的相关文章

Manthan, Codefest 16(G. Yash And Trees(dfs序+线段树))

题目链接:点击打开链接 题意:给你一棵树, 根结点为1, q组操作, 每组操作有两种, 一种是对一个结点的所有子树结点的值全部+1, 另一种是查询一个结点的子树结点上值%m的余数为素数的个数. 思路:对于第一个操作, 我们可以想到用dfs序给树重新标号, 使得一个结点的子树结点为相邻的一条线段, 这样,就可以很容易的用线段树进行处理了.  对于第二个操作, 为了维护一个区间内的值, 我们可以用bitset作为结点信息.  我们可以开一个m位的bitset, 对于每个位, 1表示这个数在此区间中,

Manthan, Codefest 19 (open for everyone, rated, Div. 1 + Div. 2)-C. Magic Grid-构造

Manthan, Codefest 19 (open for everyone, rated, Div. 1 + Div. 2)-C. Magic Grid-构造 [Problem Description] ? 给你一个\(n\),构造一个\(n\times n\)的矩阵,使其满足任意一行,或一列的异或值相同.保证\(n\)能被\(4\)整除. [Solution] ? 可以发现,从\(0\)开始,每\(4\)个连续数的异或值为\(0\),所以可以很容易使得每一行的异或值等于\(0\). ? 列

ACdream原创群赛(16) F

MST Time Limit: 2000/1000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others) SubmitStatus Problem Description Given a connected, undirected graph, a spanning tree of that graph is a subgraph that is a tree and connects all the vertices togeth

Manthan, Codefest 19 (open for everyone, rated, Div. 1 + Div. 2) (1208F,1208G,1208H)

1208 F 大意:  给定序列$a$, 求$\text{$a_i$|$a_j$&$a_k$}(i<j<k)$的最大值 枚举$i$, 从高位到低位贪心, 那么问题就转化为给定$x$, 求判断$[i+1,n]$内二进制包含$x$的个数是否不少于$2$, 可以对每个状态, 维护最远的两个位置即可. #include <iostream> #include <sstream> #include <algorithm> #include <cstdio

Manthan Codefest 19 题解

这套题还是有点质量的吧 -- 题目链接 A. XORinacci 傻叉签到题,因为异或的性质所以这个序列的循环节长度只有 \(3\) -- 查看代码 B. Uniqueness 因为序列长度乃至数的种类都不超过 \(2000\),考虑先把序列离散化. 题意让我们求一个最短的区间满足如下性质,对于每一种数,其在此区间出现次数不小于在原序列中的出现次数减 \(1\). 可以先前缀和求一下对于每种数,当前位置及之前的出现次数,和至少一共需要删掉多少个这种数,即在原序列中的出现次数减 \(1\),方便以

题解——CF Manthan, Codefest 18 (rated, Div. 1 + Div. 2) T5(思维)

还是dfs? 好像自己写的有锅 过不去 看了题解修改了才过qwq #include <cstdio> #include <algorithm> #include <cstring> #include <stack> #include <set> using namespace std; int u[200010],v[200010],n,m,k,ans; bool isdel[200010],vis[200010]; set<int>

Codeforces Manthan, Codefest 18 (rated, Div. 1 + Div. 2) D,E

D. Valid BFS? time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output The BFS algorithm is defined as follows. Consider an undirected graph with vertices numbered from 11 to nn. Initialize qq as a

Manthan, Codefest 19 (open for everyone, rated, Div. 1 + Div. 2)B(SET)

#define HAVE_STRUCT_TIMESPEC#include<bits/stdc++.h>using namespace std;int a[2007];set<int>s;int main(){ int n; cin>>n; int iend=n+1; for(int i=1;i<=n;++i) cin>>a[i]; while(iend>1&&!s.count(a[iend-1])) s.insert(a[--ie

Java笔记(16):集合框架(02)

1.ArrayList存储字符串并遍历 1 package cn.itcast_01; 2 3 import java.util.ArrayList; 4 import java.util.Iterator; 5 6 /* 7 * List的子类特点: 8 * ArrayList: 9 * 底层数据结构是数组,查询快,增删慢 10 * 线程不安全,效率高 11 * Vector: 12 * 底层数据结构是数组,查询快,增删慢 13 * 线程安全,效率低 14 * LinkedList: 15 *