1. 程式人生 > >#2009. 「SCOI2015」小凸玩密室

#2009. 「SCOI2015」小凸玩密室

神仙題啊。完全想不出

首先看方案。可以從任意一個點開始,在這個點要先走完子樹,然後走到父親,再走兄弟,再走父親的父親,父親的兄弟。。一直走到1,1的另外一個子樹,結束。

完全不會鴨.jpg

設f[i][j]是走完i的子樹,再走到i的第j個祖先的最小花費。那麼上面的方案可以表示成:f[x][1](走完x的子樹再走到x父親)+(x父親~x兄弟)+f[(x兄弟)][1](x兄弟走完了走到x父親的父親)+...

那麼怎麼求f呢,感覺不是很好求

再來一個:g[i][j]是走完i的子樹,再走到i的第j個祖先的兄弟的最小花費。

那麼就很好轉移了,分情況討論即可,不難,見程式碼



#include<bits/stdc++.h>
#define il inline
#define vd void
typedef long long ll;
il int gi(){
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
ll a[200010],b[200010],dep[200010];
ll w[200010][19];//w[i][j]是i到i的第j個祖先的距離
ll f[200010][19],g[200010][19];
int main(){
#ifndef ONLINE_JUDGE
    freopen("4253.in","r",stdin);
    freopen("4253.out","w",stdout);
#endif
    int n=gi();
    for(int i=1;i<=n;++i)a[i]=gi();
    for(int i=2;i<=n;++i)b[i]=gi();
    for(int i=1;i<=n;++i){
        dep[i]=dep[i>>1]+1;
        w[i][1]=b[i];
        for(int j=2;j<=dep[i] ;++j)
            w[i][j]=w[i>>1][j-1]+b[i];
    }
    for(int i=n;i;--i){
        for(int j=0;j<=dep[i];++j){
            if((i<<1|1)<=n){// 2個兒子,要列舉先走到哪一個兒子去
                f[i][j]=std::min(g[i<<1][0]+b[i<<1]*a[i<<1]+f[i<<1|1][j+1],g[i<<1|1][0]+b[i<<1|1]*a[i<<1|1]+f[i<<1][j+1]);
                g[i][j]=std::min(g[i<<1][0]+b[i<<1]*a[i<<1]+g[i<<1|1][j+1],g[i<<1|1][0]+b[i<<1|1]*a[i<<1|1]+g[i<<1][j+1]);
            }else if((i<<1)<=n){// 1個兒子,直接走到這個兒子然後繼續
                f[i][j]=f[i<<1][j+1]+b[i<<1]*a[i<<1];
                g[i][j]=g[i<<1][j+1]+b[i<<1]*a[i<<1];
            }else{// 葉子,子樹走完了可以直接跳到j所指向的點
                f[i][j]=w[i][j]*a[i>>j];
                g[i][j]=(w[i][j+1]+b[(i>>j)^1])*a[(i>>j)^1];
            }
        }
    }
    ll ans=2e18;
    for(int i=1;i<=n;++i){//列舉起點
        ll res=f[i][1];//先走完i的子樹,然後走到i的父親
        for(int j=i>>1,lst=i;j;lst=j,j>>=1){
            if((lst^1)<=n)res+=b[lst^1]*a[lst^1]+f[lst^1][2];
            else res+=b[j]*a[j>>1];
        }
        ans=std::min(ans,res);
    }
    printf("%lld\n",ans);
    return 0;
}