小凸玩密室
[SCOI2015]小凸玩密室
https://ac.nowcoder.com/acm/problem/20304
分析
本人感觉本题十分巧妙
由于题目中给的是完全二叉树,这引起了思考
为什么会给二叉树?保证每个节点转移只有两种情况?
为什么是完全二叉树?保证树高不超过`!
由于题目行走路线的特殊性:
假设以节点为根节点出发走
那么先走完的子树,
(有父亲的话)再回到的父亲的另一个儿子(他的兄弟)
走完兄弟之后,回到父亲的父亲的另一个儿子(父亲的兄弟)
以此类推。。。
由于以单个转移的话,十分的麻烦
所以我们会考虑到设计DP状态F[i][j]
表示走完的子树,回到第个祖先的最小花费G[i][j]
表示走完的子树,回到第个祖先的另一个儿子的最小花费
(这个DP状态太巧妙了!)
加上完全二叉树这一个优美性质
我们可以每次确定根节点之后
按定顺序走完全程,累加结果即可
时间复杂度:
代码
//P4253 #include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #define LL long long #define Cl(X,Y) memset((X),(Y),sizeof(X)) #define FOR(i,A,B) for(LL i=A;i<=B;i++) #define BOR(i,A,B) for(LL i=A;i>=B;i--) #define Lowbit(X) (X & (-X)) #define Skip cout<<endl; #define INF 0x3f3f3f3f3f3f3f3f #define Rson (X<<1|1) #define Lson (X<<1) using namespace std; const LL MaxN=2e5+10; LL Total,Num[MaxN],Fa[MaxN]; LL Child[MaxN][2]; LL F[MaxN][21],G[MaxN][21]; LL Dist[MaxN][21]; LL Ans=INF; inline void File() { freopen(".in","r",stdin); freopen(".out","w",stdout); } inline LL Bro(LL New,LL X) { return (New>>(X-1))^1; } inline LL DFS() { BOR(k,Total,1) { if(!Child[k][0]) for(LL i=1;k>>(i-1);i++) G[k][i]=(Dist[k][i]+Dist[Bro(k,i)][1])*Num[Bro(k,i)]; else if(!Child[k][1]) for(LL i=1;k>>(i-1);i++) G[k][i]=Dist[Child[k][0]][1]*Num[Child[k][0]]+G[Child[k][0]][i+1]; else for(LL i=1;k>>(i-1);i++) G[k][i]=min(Dist[Child[k][0]][1]*Num[Child[k][0]]+G[Child[k][0]][1]+G[Child[k][1]][i+1],Dist[Child[k][1]][1]*Num[Child[k][1]]+G[Child[k][1]][1]+G[Child[k][0]][i+1]); } BOR(k,Total,1) { if(!Child[k][0]) for(LL i=1;k>>(i-1);i++) F[k][i]=Dist[k][i]*Num[k>>i]; else if(!Child[k][1]) for(LL i=1;k>>(i-1);i++) F[k][i]=F[Child[k][0]][i+1]+Dist[Child[k][0]][1]*Num[Child[k][0]]; else for(LL i=1;k>>(i-1);i++) F[k][i]=min(Dist[Child[k][0]][1]*Num[Child[k][0]]+G[Child[k][0]][1]+F[Child[k][1]][i+1],Dist[Child[k][1]][1]*Num[Child[k][1]]+G[Child[k][1]][1]+F[Child[k][0]][i+1]); } FOR(k,1,Total) { LL Sum=F[k][1]; for(LL i=1,Fa=(k>>1);Fa;i++,Fa>>=1) { LL Other=Bro(k,i); if(Other>Total) Sum+=Dist[Fa][1]*Num[Fa>>1]; else Sum+=Dist[Other][1]*Num[Other]+F[Other][2]; } Ans=min(Ans,Sum); } return Ans; } signed main() { // File(); ios::sync_with_stdio(false); cin>>Total; FOR(i,1,Total) { cin>>Num[i]; } FOR(i,2,Total) { cin>>Dist[i][1]; } FOR(i,1,(Total>>1)+1) { if((i<<1)<=Total) Child[i][0]=(i<<1); else break; if((i<<1|1)<=Total) Child[i][1]=(i<<1|1); } FOR(i,2,20) for(LL k=Total;k>>i;k--) Dist[k][i]=Dist[k][i-1]+Dist[k>>(i-1)][1]; cout<<DFS()<<endl; // fclose(stdin); // fclose(stdout); //system("pause"); return 0; }