• [BZOJ3677][Apio2014]连珠线


    [BZOJ3677][Apio2014]连珠线

    试题描述

    在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做“连珠线”。不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色和蓝色。游戏开始时,只有1个珠子,而接下来新的珠子只能通过线由以下两种方式被加入:
    1.Append(w,v):一个新的珠子w和一个已有的珠子v连接,连接使用红线。 
    2.Insert(w,u,v):一个新的珠子w加入到一对通过红线连接的珠子(u,v)之间,并将红线改成蓝线。也就是将原来u连到1的红线变为u连到w的蓝线与W连到V的蓝线。 
    无论红线还是蓝线,每条线都有一个长度。而在游戏的最后,将得到游戏的最后得分:所有蓝线的长度总和。 
    现在有一个这个游戏的最终结构:你将获取到所有珠子之间的连接情况和所有连线的长度,但是你并不知道每条线的颜色是什么。 
    你现在需要找到这个结构下的最大得分,也就是说:你需要给每条线一个颜色f红色或蓝色),使得这种连线的配色方案是可以通过上述提到的两种连线方式操作得到的,并且游戏得分最大。在本题中你只需要输出最大的得分即可。

    输入

    第一行是一个正整数n,表示珠子的个数,珠子编号为1刭n。 
    接下来n-l行,每行三个正整数ai,bi(l≤ai≤10000),表示有一条长度为ci的线连接了珠子ai和珠子bi。 

    输出

    输出一个整数,为游戏的最大得分。

    输入示例

    5
    1 2 10
    1 3 40
    1 4 15
    1 5 20

    输出示例

    60

    数据规模及约定

    数据范围满足1≤n≤200000。

    题解

    一个恶心至极的树形 dp。

    我的做法大概是设状态为 f(i, j, k),i 表示我们在研究子树 i,其中 j 取 0 或 1;j = 0 时表示根在子树 i(注意这里的子树 i 都不包括节点 i)外,此时 k = 0 表示 i 不是中心点,k = 1 表示 i 是中心点;j = 1 时表示根在子树 i 中,此时 k = 0 表示 i 不是中心点,k = 1 表示 i 是中心点并且两条蓝边都在原树的子树 i 中,k = 2 表示 i 是中心点且有一条蓝边在原树的子树 i 中,另一条为 i 连向原树父亲的边。

    这个 dp 不用换根,随便选一个点 rt 开始 dp 就好了,注意最后答案是 max{ f(rt, 0, 0), f(rt, 1, 0), f(rt, 1, 1) },没有 f(rt, 0, 1) 和 f(rt, 1, 2)(想一想为什么)。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 200010
    #define maxm 400010
    #define oo (1ll << 50)
    #define LL long long
    
    int n, m, head[maxn], nxt[maxm], to[maxm], dist[maxm];
    
    void AddEdge(int a, int b, int c) {
    	to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m;
    	swap(a, b);
    	to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m;
    	return ;
    }
    
    LL f[maxn][2][3];
    void dp(int u, int fa) {
    	LL sum = 0, m1x = -oo, m00 = -oo, m1x_1 = -oo, m1x_2 = -oo, m00_1 = -oo, m00_2 = -oo;
    	int m1x_1id = -1, m00_1id = -1;
    	bool has = 0;
    	for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa) {
    		has = 1;
    		dp(to[e], u);
    		LL tmp = max(f[to[e]][0][0], f[to[e]][0][1] > -oo ? f[to[e]][0][1] + dist[e] : 0);
    		sum += tmp;
    		if(f[to[e]][1][0] > -oo) m1x = max(m1x, max(max(f[to[e]][1][0], f[to[e]][1][1]), f[to[e]][1][2] + dist[e]) - tmp);
    		m00 = max(m00, f[to[e]][0][0] - tmp);
    		LL t1x = max(f[to[e]][1][0], f[to[e]][1][1]) + dist[e];
    		if(f[to[e]][1][0] > -oo) {
    			if(t1x - tmp > m1x_1) m1x_2 = m1x_1, m1x_1 = t1x - tmp, m1x_1id = to[e];
    			else if(t1x - tmp > m1x_2) m1x_2 = t1x - tmp;
    		}
    		int t00 = f[to[e]][0][0] + dist[e];
    		if(t00 - tmp > m00_1) m00_2 = m00_1, m00_1 = t00 - tmp, m00_1id = to[e];
    		else if(t00 - tmp > m00_2) m00_2 = t00 - tmp;
    	}
    	if(!has) {
    		f[u][0][1] = f[u][1][0] = f[u][1][1] = f[u][1][2] = -oo;
    		return ;
    	}
    	f[u][0][0] = sum;
    	if(m00_2 > -oo || fa) f[u][0][1] = sum + m00_1;
    	f[u][1][0] = sum + max(m00, m1x);
    	if(m00_2 > -oo) f[u][1][1] = sum + max(m00_1 + m00_2, max(m00_1 + (m00_1id == m1x_1id ? m1x_2 : m1x_1), m1x_1 + (m1x_1id == m00_1id ? m00_2 : m00_1)));
    	if(fa) f[u][1][2] = sum + max(m00_1, m1x_1);
    //	printf("%d: %d %d | %d %d %d
    ", u, f[u][0][0], f[u][0][1], f[u][1][0], f[u][1][1], f[u][1][2]);
    	return ;
    }
    
    int main() {
    	n = read();
    	for(int i = 1; i < n; i++) {
    		int a = read(), b = read(), c = read();
    		AddEdge(a, b, c);
    	}
    	
    	dp(1, 0);
    	
    	printf("%d
    ", (int)max(max(f[1][1][0], f[1][1][1]), f[1][0][0]));
    	
    	return 0;
    }
    
  • 相关阅读:
    MacOS的多重启动工具
    exchange 2003配置ASSP 反垃圾邮件
    VMWARE workstation 9 收缩虚拟硬盘
    [AX2012]在SSRS报表中获取从Menuitem传入的记录
    [AX2012]代码更改默认财务维度
    js使用模板快速填充数据
    CI框架--事务
    $().each 和表单事件的坑
    基于layerpage 前后端异步分页
    SQL 语句格式
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/6859831.html
Copyright © 2020-2023  润新知