小凸玩密室

[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;
}
全部评论

相关推荐

伟大的烤冷面被普调:暨大✌🏻就是强
点赞 评论 收藏
分享
5 收藏 评论
分享
牛客网
牛客企业服务