考场没做出来。
赛后看了讲解PPT,补了,是一道很妙的题,所以来写一篇笔记。
尝试过交题解,但不给过,又见题解区已经有一篇非常好的非官方题解,个人也感觉写的太草也太抽象,达不到题解要求,就不占TA风头了,这仅仅是一篇笔记。
步入正题了
此题首先要注意一点:
不开long long见祖宗!(连挂两次)
正解:
首先考虑最优策略:
当一个点的两个子树未被装满时,两棵树的电荷显然差\(1\)或相等。
然后考虑神明的最优策略:显然要让球总是尽可能远离目标点。
所以相等时神明总是要选择远离目标方向。
所以当两棵子树差 \(1\) 时是凡人的唯一机会,此时可以让球相对靠近目标点。
那怎么做呢?
不妨考虑两次投球时球的电荷,倘若两棵子树未满。
当电荷不同时显然会同时落在一棵子树;
当电荷相同时则两个球必然会落在不同子树,
也就必然会有一个球相对靠近目标点,而这个球显然可以是当两棵子树电荷差 \(1\) 时投下的那个球。
所以凡人的一种最优策略显然是一直投正电或负电。
这样必然能保证两棵子树差 \(1\) 时让球相对靠近目标点。
这种策略确实一时很难理解,如果想更好地理解这种思路,可以将凡人和神明的最优策略带入样例模拟,这样很快就能明白。
一直投同一种电荷,显然让问题变成了模拟。
首先,球要填满目标点的子树。
然后发现:
设当前需要 \(ans\) 个球坠向目标点, \(c\) 表示旁边子树的大小,
则旁边的子树会落下 \(min(ans,c)\) 个球,新的 \(ans\) 显然是 \(ans+min(ans,c)\) 。
所以考虑不断从目标节点向上跳直到到了根节点不能再跳,每次更新 \(ans\) 。
最后就输出答案。
复杂度撑死是 \(O(n^2)\) ,也就是一条链,满足题意。
码
//writer:Oier_szc
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1005;
int n;
int fa[N],ch[N][3],c[N],sz[N];
void get_size(int u)
{
sz[u]=c[u];
for(int i=1;i<=ch[u][0];++i)
{
int to=ch[u][i];
get_size(to);
sz[u]+=sz[to];
}
}
signed main()
{
scanf("%lld",&n);
for(int i=2;i<=n;++i)
{
scanf("%lld",&fa[i]);
ch[fa[i]][++ch[fa[i]][0]]=i;
}
for(int i=1;i<=n;++i) scanf("%lld",&c[i]);
get_size(1);
for(int i=1;i<=n;++i)
{
int now=i,ans=sz[i];
while(fa[now])
{
int this_ch=now,other_ch=-1;
now=fa[now];
for(int j=1;j<=ch[now][0];++j)
{
if(ch[now][j]!=this_ch) other_ch=ch[now][j];
}
if(other_ch==-1) continue;
//other_ch==兄弟节点
ans+=min(sz[other_ch],ans);
}
printf("%lld ",ans);
}
return 0;
}
标签:子树,洛谷,int,题解,电荷,P8966,笔记,long,ans
From: https://www.cnblogs.com/StevenZC/p/17085238.html