• 题解 [APIO2014]连珠线


    题目传送门

    题目大意

    在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为 (1)(n)。这个游戏从一个珠子开始,每次会用如下方式添加一个新的珠子:

    Append(w, v):一个新的珠子 (w) 和一个已经添加的珠子 (v) 用红线连接起来。

    Insert(w, u, v):一个新的珠子 (w) 插入到用红线连起来的两个珠子 (u,v) 之间。具体过程是删去 (u,v) 之间红线,分别用蓝线连接 (u,w)(w,v)

    每条线都有一个长度。游戏结束后,你的最终得分为蓝线长度之和。

    给你连珠线游戏结束后的游戏局面,只告诉了你珠子和链的连接方式以及每条线的长度,没有告诉你每条线分别是什么颜色。

    你需要写一个程序来找出最大可能得分。即,在所有以给出的最终局面结束的连珠线游戏中找出那个得分最大的,然后输出最大可能得分。

    (nle 10^6)

    思路

    首先我们通过思考发现这样一个结论:

    当树的形态固定的时候,一个点作为中间点的情况当且仅当它的某个儿子连向他,他连向他爸爸

    然后我们可以设 (f_{u,0/1}) 表示以 (u) 为根的子树 (u) 这个点是否是中间点的最大贡献。然后我们可以得到转移式:

    [f_{u,0} o sum_{vin son_u} max{f_{v,1}+w,f_{u,0}} ]

    [f_{u,1} o max_{vin son_u} {f_{u,0}-max{f_{v,1}+w,f_{v,0}}+f_{v,0}+w} ]

    然后答案就是 (f_{root,0})。枚举每个根做一遍 ( ext{dp}) 可以获得 (Theta(n^2) & 40) 的好成绩。

    然后你发现我们这个东西其实是可以换根的,我们只需要维护一个前缀最大值、后缀最大值即可。具体见代码。

    时间复杂度 (Theta(n))

    ( exttt{Code})

    #include <bits/stdc++.h>
    using namespace std;
    
    #define Int register int
    #define INF 0x3f3f3f3f
    #define MAXN 200005
    
    template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
    template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
    template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
    
    int toop = 1,to[MAXN << 1],wei[MAXN << 1],nxt[MAXN << 1],head[MAXN];
    void Add_Edge (int u,int v,int w){
    	to[++ toop] = v,wei[toop] = w,nxt[toop] = head[u],head[u] = toop;
    	to[++ toop] = u,wei[toop] = w,nxt[toop] = head[v],head[v] = toop;
    }
    
    int n,ans,f[MAXN][2];
    vector <int> son[MAXN],len[MAXN];
    vector <int> pre[MAXN],suf[MAXN];
    
    void dfs1 (int u,int fa){
    	f[u][0] = 0,f[u][1] = -INF;
    	for (Int i = head[u];i;i = nxt[i]) if (to[i] ^ fa) son[u].push_back (to[i]),len[u].push_back (wei[i]);
    	for (Int i = 0;i < son[u].size();++ i){
    		int v = son[u][i],w = len[u][i];
    		dfs1 (v,u),f[u][0] += max (f[v][0],f[v][1] + w);
    		pre[u].push_back (-max (f[v][0],f[v][1] + w) + f[v][0] + w);
    		suf[u].push_back (-max (f[v][0],f[v][1] + w) + f[v][0] + w);  
    	}  
    	for (Int i = 0;i < son[u].size();++ i) f[u][1] = max (f[u][1],f[u][0] + pre[u][i]);
    	for (Int i = 1;i < son[u].size();++ i) pre[u][i] = max (pre[u][i],pre[u][i - 1]);
    	for (Int i = son[u].size() - 2;i >= 0;-- i) suf[u][i] = max (suf[u][i],suf[u][i + 1]);
    }
    
    void dfs2 (int u,int fa,int ff0,int ff1,int ffl){
    	f[u][0] += max (ff0,ff1 + ffl),f[u][1] += max (ff0,ff1 + ffl);
    	f[u][1] = max (f[u][1],f[u][0] - max (ff0,ff1 + ffl) + ff0 + ffl);
    	ans = max (ans,f[u][0]);//以u为根的时候显然不能作为中点 
    	for (Int i = 0;i < son[u].size();++ i){
    		int v = son[u][i],w = len[u][i];
    		int newf0 = f[u][0] - max (f[v][0],f[v][1] + w);
    		int delta = -max (ff0,ff1 + ffl) + ff0 + ffl;
    		if (i != 0) delta = max (delta,pre[u][i - 1]);
    		if (i < son[u].size() - 1) delta = max (delta,suf[u][i + 1]);
    		dfs2 (v,u,newf0,newf0 + delta,w);
    	}
    } 
    
    signed main(){
    	read (n);
    	for (Int i = 2,u,v,w;i <= n;++ i) read (u,v,w),Add_Edge (u,v,w);
    	dfs1 (1,0),dfs2 (1,0,0,-INF,-INF);
    	write (ans),putchar ('
    '); 
    	return 0;
    }
    /*
    dp[u][0]=sum_{vin son_u} max{dp[v][0],dp[v][1]+w}
    dp[u][1]=dp[u][0]+max_{vin son_u} (-max(dp[v][0],dp[v][1]+w)+dp[v][0]+w)
    */
    
  • 相关阅读:
    LeetCode | Divide Two Integers
    LeetCode | Pow(x, n)
    LeetCode | Sqrt (x)
    LeetCode | 3 Sum
    LeetCode | Two Sum
    LeetCode | Pascal's Triangle II
    nodejs eclipse
    CentOS: Make Command not Found and linux xinetd 服务不能启动
    jquery将form表单序列化常json
    VMware Mac OS补丁安装
  • 原文地址:https://www.cnblogs.com/Dark-Romance/p/13724739.html
Copyright © 2020-2023  润新知