bzoj 1825: [JSOI2010]蔬菜庆典

1825: [JSOI2010]蔬菜庆典

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 112  Solved: 45
[Submit][Status][Discuss]

Description

Input

Output

对于每组数据,输出一行。若蔬菜的总价能无限制增大,输出"+inf"(不含引号)。否则输出一个整数,表示所有蔬菜的最大总价。

Sample Input

5
-1 3
1 2
1 1
3 2
3 2
5
-1 3
1 2
1 1
3 2
3 3
0

Sample Output

13
+inf

HINT

 


简化版题意:

给出一棵有根树,每个点有一个权值v,可以对一个有父亲并且又有儿子的节点选择一个儿子进行一次操作使$v[x]=v[fa[x]]+v[son[x]]-v[x]$。

问无限制使用这种操作后树上的权值和最大为多少。

 

参照样例,显然当某一时刻一个非根节点有两个不同权值的儿子时答案就是正无穷。

假设这个点的权值为A,父亲为B,权值小的孩子为C,大的为D。

第一次 A'=(B+C-A)  第二次 A''=B+D-A'=B+D-B-C+A=A+D-C>A

 

然后考虑怎么构造出这种情况。

因为根节点是不会变的,所以对于1的每一个儿子可以分开处理,设为函数solve(x)。

如果某个点有两个儿子不同那么就不需要构造直接返回inf。

如果每个点的$v[fa[x]]+v[son[x]]$都等于$2v[x]$显然这棵树的权值是固定的,返回整棵树的权值。

那么现在存在一个点$v[fa[x]]+v[son[x]]!=2[v[x]]$,这个点的权值可以变化,那么说明这棵树的非叶节点的权值都可以变化(递归考虑,一个点的权值要么本身就能变化,那么它父亲或儿子变化后它可以变化),此时如果答案不为inf,这棵树只能是一条链,或者链上的最后一个点可以有多个叶儿子。

两种情况没有区别,考虑一条链怎么做。

不妨让它长这样:

....A B C.....

对B操作,我们从两个视角看:

A:  B'=A+(C-B)

C:  B'=C-(B-A)

B'-A=(C-B)

C-B'=(B-A)

其实就是差分序列的相邻两项交换了位置。

然后做法就很明了了。。。

为了让和尽量大,我们把差分序列排个序就好啦>_<

 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 200005
#define int long long
#define inf 0x3f3f3f3f
using namespace std;
int head[N],ver[N],nxt[N],tot;
int son[N];
void add(int a,int b)
{
	tot++;nxt[tot]=head[a];head[a]=tot;ver[tot]=b;return ;
}
int n;
int p[N],v[N];
bool flag,lian,flag2,flag3;
int sum[N],ss;
void dfs(int x)
{
	int pre=inf;
	if(!head[x])ss+=v[x];
	if(p[x]!=1&&son[x]!=0&&son[p[x]]>=2)flag3=1;
	for(int i=head[x];i;i=nxt[i])
	{
		if(son[x]>1)lian=1;
		if(pre!=inf&&v[ver[i]]!=pre)flag=1;
		if(v[p[x]]+v[ver[i]]!=2*v[x])flag2=1;
		pre=v[ver[i]];
		dfs(ver[i]);
		sum[x]+=sum[ver[i]];
	}
	sum[x]+=v[x];
	return ;
}
int st[N],top;
bool cmp(int x,int y)
{
	return x>y;
}
int solve(int x)
{
	flag=0;lian=0;flag2=0;flag3=0;ss=0;
	dfs(x);
	if(flag||(lian&&flag2&&flag3))return 0;
	if(lian&&flag3)return sum[x];
	top=0;
	for(int i=x;;i=ver[head[i]])
	{
		st[++top]=v[i]-v[p[i]];
		if(!head[i])break;
	}
	sort(st+1,st+top+1,cmp);
	int now=v[1];
	int sm=0;
	for(int i=1;i<top;i++)
	{
		now+=st[i];
		sm+=now;
	}
	return sm+ss;
}
signed main()
{
	while(~scanf("%lld",&n))
	{
		if(n==0)break;
		for(int i=1;i<=n;i++)
		{
			scanf("%lld%lld",&p[i],&v[i]);
			if(p[i]!=-1)add(p[i],i),son[p[i]]++;
		}
		int ans=0,ts=0;
		for(int i=head[1];i;i=nxt[i])
		{
			int tmp=solve(ver[i]);
			if(flag||(lian&&flag2&&flag3))ts=1;
			ans+=tmp;
		}
		if(ts)puts("+inf");
		else printf("%lld\n",ans+v[1]);
		tot=0;
		for(int i=1;i<=n;i++)head[i]=sum[i]=p[i]=v[i]=son[i]=0;
	}
	return 0;
}

  

 

posted @ 2017-06-08 16:02  SD_le  阅读(270)  评论(0编辑  收藏  举报
重置按钮