jzoj1899. 剪枝

题目描述

Description
  给出一棵有根树。树有n个结点,被分别标记成1到n的整数,1号结点为根结点。第i(1≤i≤n)个结点的权值为Wi。对于结点i,它有Ti个孩子,从左到右依次为Pi1,Pi2,…,PiTi。特别地,若i号结点是叶结点,则Ti=0。
  我们对树进行深度优先搜索(DFS),每个点必须按从左到右的顺序访问每个孩子,形成一个DFS序列,记作Seq{Seq1,Seq2,…,Seqn}。对于两个叶结点a、b,我们说它们是相邻的,当且仅当不存在另外的叶结点c,在DFS序列中c在a、b之间。换个方式讲,对于叶结点a、b、c,记Seqi=a,Seqj=b(i<j) ,不存在Seqk=c,使得i<k<j。
  每对相邻的叶结点(a,b),都存在一个影响值。影响值定义为a到b的路径上(不包含a、b的结点)的最大点权值。
  定义一棵树的价值等于这棵树所有叶结点的权值之和减去每对相邻叶结点的影响值。
  当然,要是让你算这棵树的价值就太简单了。你的目标是对树进行一些剪枝,使树的价值最大。剪枝的方式为:如果一个结点的孩子都是叶结点,就可以将它所有的孩子剪去。

Input
  第1行:n;
  第2…n+1行:第i+1行为Wi,Ti,Pi1,Pi2,…,PiTi。

Output
  第1行:这棵树修改后的最大价值。

Sample Input
输入样例一】
3
1 2 2 3
2 0
2 0

【输入样例二】
8
4 2 2 3
2 3 4 5 6
3 2 7 8
5 0
4 0
2 0
1 0
1 0

Sample Output
【输出样例一】
3

【输出样例二】
6

Data Constraint

Hint
【数据范围】
  对于20%的数据,n≤20;
  对于60%的数据,n≤2000;
  对于100%的数据,n≤100000,0<Wi≤10000。

题解

n年前做过但还是没切
每次考虑相邻两个叶子,从u–lca转移到v–lca,用max单调转移

code

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
using namespace std;

int a[100001][2];
int ls[100001];
int son[100001];
int w[100001];
int W[100001];
int fa[100001];
int bg[100001];
int ed[100001];
int d[100001];
int f[100001];
int b[100001];
int c[100001];
int sum[100002];
int Sum[100002];
int n,m,i,j,k,l,len,tot,l1,l2,mx,ans;

void New(int x,int y)
{
	++len;
	a[len][0]=y;
	a[len][1]=ls[x];
	ls[x]=len;
}

void dfs(int t)
{
	int i;
	
	if (!ls[t])
	d[++tot]=t;
	
	bg[t]=++j;
	
	for (i=ls[t]; i; i=a[i][1])
	{
		fa[a[i][0]]=t;
		dfs(a[i][0]);
	}
	
	ed[t]=j;
}

bool pd(int x,int y)
{
	return bg[x]<=bg[y] && ed[y]<=ed[x];
}

bool swap(int &x,int &y)
{
	int z=x;
	x=y;
	y=z;
}

int main()
{
//	freopen("cut2.in","r",stdin);
//	freopen("S8_13_3.in","r",stdin);
	
	scanf("%d",&n);
	fo(i,1,n)
	{
		scanf("%d%d",&w[i],&m);
		fo(j,1,m)
		scanf("%d",&son[j]);
		
		fd(j,m,1)
		New(i,son[j]);
	}
	
	j=0;
	dfs(1);
	
	i=d[1];
	while (i)
	{
		f[i]=w[i];
		i=fa[i];
	}
	
	fo(l,2,tot)
	{
		l1=0;
		l2=0;
		
		i=d[l-1];
		while (!pd(i,d[l]))
		{
			b[++l1]=i;
			i=fa[i];
		}
		
		i=d[l];
		while (!pd(i,d[l-1]))
		{
			c[++l2]=i;
			i=fa[i];
		}
		
		fd(i,l1/2,1) swap(b[i],b[l1-i+1]);
		fd(i,l2/2,1) swap(c[i],c[l2-i+1]);
		
		b[0]=fa[b[1]];
		sum[0]=-2133333333;
		W[0]=w[b[0]];
		fo(i,1,l1)
		{
			sum[i]=max(sum[i-1],f[b[i]]);
			W[i]=max(W[i-1],w[b[i]]);
		}
		
		Sum[l1+1]=-2133333333;
		fd(i,l1,1)
		Sum[i]=max(Sum[i+1],f[b[i]]-W[i-1]);
		
		mx=w[b[0]];
		j=1;
		fo(i,1,l2)
		{
			while (j<l1 && w[b[j]]<=mx)
			++j;
			
			f[c[i]]=max(sum[j]-mx,Sum[j+1])+w[c[i]];
			
			mx=max(mx,w[c[i]]);
		}
	}
	
	i=d[tot];
	while (i)
	{
		ans=max(ans,f[i]);
		i=fa[i];
	}
	
	printf("%d\n",ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章