Description
有 N 个农民, 他们住在 N 个不同的村子里. 这 N 个村子形成一棵树.
每个农民初始时获得 X 的钱.
每一次操作, 一个农民可以从它自己的钱中, 取出任意数量的钱, 交给某个相邻村子的农民.
对于每个农民给定一个值 v_i, 求
(1) 最少需要多少次操作, 使得每个农民最终拿到的钱 >= 给定的值.
Solution
这种题都被出烂了……但是我还是思考了挺久,写了挺久。
就是状态设计要体现贪心,
f
i
,
j
f_{i,j}
fi,j表示以
i
i
i为根子树,至少使除了
i
i
i外的所有点满足条件,操作了
j
j
j次最多向上贡献多少钱,转移就是个树形背包,这样按
s
i
z
e
size
size合并相当于枚举点对,因为每对点只会在
L
C
A
LCA
LCA处被枚举到,所以复杂度为
n
2
n^2
n2。
Code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=2010;
const int inf=1044266559;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
void upd(int&x,int y){x=max(x,y);}
int n,X,v[Maxn],f[Maxn][Maxn],sz[Maxn],g[Maxn],h[Maxn];//f[i][j]操作j次最多向上贡献多少
struct Edge{int y,next;}e[Maxn<<1];
int last[Maxn],len=0;
void ins(int x,int y)
{
int t=++len;
e[t].y=y;e[t].next=last[x];last[x]=t;
}
void dfs(int x,int fa)
{
bool flag=true;sz[x]=1;
for(int i=last[x];i;i=e[i].next)
{
int y=e[i].y;
if(y==fa)continue;
dfs(y,x);flag=false;
}
if(flag)
{
if(v[x]<0)f[x][0]=v[x];
else f[x][0]=0,f[x][1]=v[x];
return;
}
memset(g,-63,sizeof(g));
bool fir=true;
for(int i=last[x];i;i=e[i].next)
{
int y=e[i].y;
if(y==fa)continue;
if(fir)
{
fir=false;
for(int j=0;j<=sz[y];j++)
{
if(f[y][j]==-inf)continue;
if(f[y][j]<0)g[j+1]=f[y][j];
else g[j]=f[y][j];
}
}
else
{
for(int j=0;j<=sz[x]+sz[y];j++)h[j]=-inf;
for(int j=0;j<=sz[x];j++)
{
if(g[j]==-inf)continue;
for(int k=0;k<=sz[y];k++)
{
if(f[y][k]==-inf)continue;
upd(h[j+k+(f[y][k]<0)],g[j]+f[y][k]);
}
}
for(int j=0;j<=sz[x]+sz[y];j++)g[j]=h[j];
}
sz[x]+=sz[y];
}
for(int j=0;j<=sz[x];j++)
{
if(g[j]==-inf)continue;
if(g[j]+v[x]>=0)upd(f[x][j],0),upd(f[x][j+1],g[j]+v[x]);
else upd(f[x][j],g[j]+v[x]);
}
}
int main()
{
memset(f,-63,sizeof(f));
n=read(),X=read();
for(int i=1;i<=n;i++)v[i]=X-read();
for(int i=1;i<n;i++){int x=read(),y=read();ins(x,y),ins(y,x);}
dfs(1,0);
for(int i=0;i<=n;i++)if(f[1][i]>=0)return printf("%d",i),0;
}