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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章