P4253 [SCOI2015] 小凸玩密室

将状态刻画为 \(f_{i,j}\) 表示点亮完 \(i\) 的子树,然后去到 \(j\) 的最小代价。转移是好做的。每个点再点亮完祂的子树后只有可能去到祂的祖先或祖先的儿子,因为给出的树是完全二叉树,所以状态数是 \(n\log(n)\)
但是题目有一个很耐受的条件:可以任选一个点开始。考虑枚举起点,然后暴力向上跳,并把另一个子树的信息合并上来,由于树高为 \(\log(n)\) 所以时间复杂度还是 \(n\log(n)\)。但是我在实现的时候用了 map 所以总时间复杂度变成了 \(n\log^2(n)\)

代码

#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
#define int long long
const int maxn=200010;
int n,a[maxn],b[maxn],deep[maxn],sy[maxn];
map<int,int>f[maxn];
int dfs(int u,int mb){
    if(f[u].find(mb)!=f[u].end()) return f[u][mb];
    int ls=u<<1,rs=u<<1|1,ans,ans2;
    if(ls>n) return deep[u]*a[mb];
    if(rs>n) return deep[ls]*a[mb]+a[ls]*b[ls];
    {
        ans=dfs(ls,rs);
        ans+=a[ls]*b[ls];
        ans+=deep[rs]*a[rs]-2*a[rs]*deep[u];
        ans+=dfs(rs,mb);
    }
    {
        ans2=dfs(rs,ls);
        ans2+=a[rs]*b[rs];
        ans2+=deep[ls]*a[ls]-2*a[ls]*deep[u];
        ans2+=dfs(ls,mb);
    }
    return f[u][mb]=min(ans,ans2);
}
void get_deep(int u){
    if((u<<1)<=n) deep[u<<1]=deep[u]+b[u<<1],get_deep(u<<1);
    if((u<<1|1)<=n) deep[u<<1|1]=deep[u]+b[u<<1|1],get_deep(u<<1|1);
}
int js(int u){
    int fa=(u>>1),ans=dfs(u,fa)-deep[fa]*a[fa];
    int lt=(u&1);
    u=fa;
    while(u){
        int fa=(u>>1);
        int ls=(u<<1),rs=(u<<1|1);
        if(lt){
            if(ls<=n){
                ans+=a[ls]*b[ls];
                ans+=dfs(ls,fa)-deep[fa]*a[fa];
            }
            else ans+=a[fa]*b[u];
        }
        else{
            if(rs<=n){
                ans+=a[rs]*b[rs];
                ans+=dfs(rs,fa)-deep[fa]*a[fa];
            }
            else ans+=a[fa]*b[u];
        }
        lt=(u&1);
        u=fa;
    }
    return ans;
}
signed main(){
    read(n);
    if(n==1){
        write(0);
        return 0;
    }
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=2;i<=n;i++) read(b[i]);
    if(n==2){
        write(min(a[1]*b[2],a[2]*b[2]));
        return 0;
    }
    get_deep(1);
    int ans=1000000000000000000;
    for(int i=1;i<=n;i++) ans=min(ans,js(i));
    write(ans);
    return 0;
}
posted @ 2026-01-14 21:57  Link-Cut_Trees  阅读(5)  评论(0)    收藏  举报