先说点题外话,我觉得二分也是完全ok的,但是在测试点六溢出了…
后来证明算法是正确的,数据卡了二分…设置以下不让 f f f数组溢出就行了
思路就是二分最大值,而后假定所有叶子节点都是最大值,向上合并
这样就知道每个节点有多少居民是上面传递下来的,多了没事,但如果少了
就存在节点的居民没分配完的情况,就是不合法的
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=4e5+10;
int t,n,mid,in[maxn],l,r;
struct edge{
int to,nxt;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v){
d[++cnt]=(edge){v,head[u]},head[u]=cnt;
}
int f[maxn],a[maxn],flag;
void dfs(int u,int fa)
{
if( !flag ) return;
int ok=0; f[u]=0;
for(int i=head[u];i;i=d[i].nxt )
{
int v=d[i].to;
ok=1; dfs(v,u);
if( f[u]<=1e17 )
f[u]+=f[v];
}
if( ok==0 ) f[u]=mid-a[u];
else
{
f[u]-=a[u];
if( f[u]<0 ) flag=0;
}
}
bool isok(int mid)
{
flag=1;
dfs(1,0);
return flag;
}
signed main()
{
cin >> n;
int s;
for(int i=2;i<=n;i++)
{
int x; scanf("%lld",&x);
add(x,i);
in[i]++,in[x]++;
}
l=0,r=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
r += a[i];
}
for(int i=2;i<=n;i++)
if( in[i]==1 ) l=max(l,a[i]);
int ans=0;
while( r>=l )
{
mid = l+r>>1;
if( isok(mid) ) r=mid-1,ans=mid;
else l=mid+1;
}
cout << ans;
}
标称的做法
如果只有根节点有居民,答案是 [ a 1 / l e a f 1 ] [a_1/leaf_1] [a1/leaf1]向上取整
意思是 a 1 a_1 a1个居民分配到 l e a f leaf leaf个叶子节点去
但是情况不是这样,有些节点本身就有比较多的居民
预处理 s u m i sum_i sumi为节点 i i i为根的子树内的总居民数量
l e a f i leaf_i leafi为节点 i i i为根的子树内的总叶子节点数量
假如最后最大的居民在以 u u u为根的子树内
Ⅰ . \color{Red}Ⅰ. Ⅰ.
如果存在一个儿子 v v v,使得就算不给 v v v分配一个居民,最后还是 v v v子树内的叶子节点居民最大,那么就把问题规模缩小成以 v v v为根的子树了(其他儿子就没用了)
Ⅱ . \color{Red}Ⅱ. Ⅱ.
如果不存在这种儿子 v v v,就存在一种分配方式使得两边尽量平均
此时在 u u u的子树内最大的叶子节点就是 [ s u m i / l e a f i ] [sum_i/leaf_i] [sumi/leafi]
所以答案就是最大的 [ s u m i / l e a f i ] [sum_i/leaf_i] [sumi/leafi]
因为所有的情况一最后都变成了情况二
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
#define int long long
struct edge{
int to,nxt;
}d[maxn]; int n,head[maxn],cnt=1;
void add(int u,int v){
d[++cnt]=(edge){v,head[u]},head[u]=cnt;
}
int sumn[maxn],leaf[maxn],a[maxn];
void dfs(int u)
{
int ok=0;
sumn[u] = a[u];
for(int i=head[u];i;i=d[i].nxt )
{
int v=d[i].to;
dfs(v); ok=1;
leaf[u]+=leaf[v];
sumn[u]+=sumn[v];
}
if( !ok ) leaf[u]=1;
}
signed main()
{
cin >> n;
for(int i=2;i<=n;i++)
{
int x; scanf("%lld",&x);
add(x,i);
}
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
int ans=0;
dfs(1);
for(int i=1;i<=n;i++)
ans = max( ans,sumn[i]/leaf[i]+(sumn[i]%leaf[i]!=0) );
cout << ans;
return 0;
}