标签:max cstring 比较 完全二叉树 题目 break solution for 推广
题目
当然,根固定为\(1\),但是第一个被点亮的灯不一定是\(1\)。
这里我只会讲最终做法,但是如果你要问这个结果到底是怎么得到的,其中的心路历程是什么,这篇博客:https://www.luogu.com.cn/blog/MachineryCountry/solution-p4253相信能给你不错的体验。
首先,我们先观察叶子节点,一个叶子节点,跑完之后,要么点亮其的一个祖先(会出现这种情况一般是因为第一个被点亮的灯不是\(1\)),要么点亮其祖先的另外一个儿子,推广发现每一个点在遍历完其子树后,都是这种情况。
又发现,完全二叉树有一个极其优美的性质,树的高度为\(logn\)层。
不妨考虑\(f_{i,j}\)表示第\(i\)个子树遍历完之后点亮的是第\(j+1\)个祖先的最小花费,\(g_{i,j}\)则是第\(j+1\)个祖先的一个儿子的最小花费。
\(g_{i,j}=min(a_{lc}*b_{lc}+g_{lc,0}+g_{rc,j+1},a_{rc}*b_{rc}+g_{rc,0}+g_{lc,j+1})\)
\(f_{i,j}=min(a_{lc}*b_{lc}+g_{lc,0}+f_{rc,j+1},a_{rc}*b_{rc}+g_{rc,0}+f_{lc,j+1})\)
其余的状态方程就不列了,比较麻烦。
我还专门设置了\(t_{i}\)表示遍历\(i\)的子树的花费。
但是需要注意的是,不管是\(f,g,t\),\(i\)号点被点亮的费用都是没有算进去的,因为遍历\(i\)的子树,其子树内的点\(x\)肯定都是从另外一个\(i\)子树内的点\(y\)出发去亮\(x\)的,因此可以树形\(DP\)直接计算,但是\(x\)不一样,\(x\)是灵活应变的,只有外面知道他是被哪个点点亮的,而树形DP基本上只能处理子树内的点,所以只能不算点亮\(x\)的花费。(事实上,也正是因为树形DP只能处理子树内的点,所以从\(i\)子树出来到达的点还必须多设置一维来处理)
时间复杂度:\(O(nlogn)\).
其实我的代码打复杂了,如果想要看代码好看一些的可以去你谷的题解区看看,里面的许多代码都写的比我优美。
#include<cstdio>
#include<cstring>
#define N 210000
#define SN 20 //17
using namespace std;
typedef long long LL;
template<class T>
inline T mymin(T x,T y){return x<y?x:y;}
template<class T>
inline T mymax(T x,T y){return x>y?x:y;}
int fa[N][SN],other[N]/*另外一个儿子*/,n;
LL f[N][SN],g[N][SN],dis[N],a[N],b[N],t[N];
void dfs(int x)
{
for(int i=1;i<=17;i++)
{
fa[x][i]=fa[fa[x][0]][i-1];
if(!fa[x][i])break;
}
for(int i=0;i<=1;i++)
{
int y=(x<<1)+i;
if(y>n)continue;
dis[y]=dis[x]+b[y];fa[y][0]=x;
}
for(int i=0;i<=1;i++)
{
int y=(x<<1)+i;
if(y>n)continue;
other[x]=y^1;
dfs(y);
}
for(int i=0;i<=17;i++)
{
if(!fa[x][i])break;
if((x<<1)>n)
{
g[x][i]=(dis[other[fa[x][i]]]+dis[x]-2*dis[fa[x][i]])*a[other[fa[x][i]]];
f[x][i]=(dis[x]-dis[fa[x][i]])*a[fa[x][i]];
}
else if((x<<1)==n)//只有一个儿子
{
int lc=x<<1;
g[x][i]=b[lc]*a[lc]+g[lc][i+1];
f[x][i]=b[lc]*a[lc]+f[lc][i+1];
}
else//两个儿子都有
{
int lc=x<<1,rc=(x<<1)+1;
g[x][i]=mymin(b[lc]*a[lc]+g[lc][0]+g[rc][i+1],b[rc]*a[rc]+g[rc][0]+g[lc][i+1]);
f[x][i]=mymin(b[lc]*a[lc]+g[lc][0]+f[rc][i+1],b[rc]*a[rc]+g[rc][0]+f[lc][i+1]);
}
}
if((x<<1)>n)t[x]=0;
else if((x<<1)==n)t[x]=t[x<<1]+b[x<<1]*a[x<<1];
else
{
int lc=x<<1,rc=(x<<1)+1;
t[x]=mymin(b[lc]*a[lc]+g[lc][0]+t[rc],b[rc]*a[rc]+g[rc][0]+t[lc]);
}
}
inline int pd_son(int x,int f){return (f<<1)==x?0:1;}
LL solve(int x)//假设x为根,且x不是1号点
{
LL ans=f[x][0];
int ff=(x>>1);
while(ff>1)
{
int y=x^1/*x的兄弟*/;
if(y>n/*x是ff的唯一儿子*/)ans+=b[ff]*a[ff>>1];
else ans+=f[y][1]+a[y]*b[y];
x=ff;ff=(x>>1);
}
int y=x^1/*x的兄弟*/;
if(y<=n/*x是ff的唯一儿子*/)ans+=t[y]+a[y]*b[y];
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=2;i<=n;i++)scanf("%lld",&b[i]);
dfs(1);
LL ans=t[1];
for(int i=2;i<=n;i++)
{
ans=mymin(ans,solve(i));
}
printf("%lld\n",ans);
return 0;
}
标签:max cstring 比较 完全二叉树 题目 break solution for 推广
原文地址:https://www.cnblogs.com/zhangjianjunab/p/13846068.html