洛谷P4253:[SCOI2015]小凸玩密室 (DP)

题目传送门:https://www.luogu.org/problemnew/show/P4253


题目分析:模拟赛的时候因为T2的锅,就没仔细想这题,写了个20pts的暴力状压还错了。后来改的时候发现我的code在跳到当前节点i的第一个子树未满的祖先j时,忘记查看j自身是否被点亮了QAQ。

这题的正解其实也不难,主要是一些细节比较烦。由于题面给出的本身就是棵二叉树,可以直接记f[node][i]表示以node为第一个点亮的节点,并且最后点亮的是node子树中的叶子结点i时,点亮node子树的最小花费。很明显f数组的总大小是nlog(n)的。合并的时候在node的Lson中找一个i使得f[Lson][i]+Len(i,Rson)a[Rson]最小,加上Len(node,Lson)a[Lson],记为temp1。然后用每个f[Rson][x]加上temp1即变为f[node][x]。对Lson中每个叶子结点x的处理也同理。这样维护f的时间就是O(nlog(n))

统计答案也类似。对每个节点维护g[node][0],表示从node的左子树中选一个x使得Len(node,Lson)a[Lson]+f[Lson][x]+Len(x,fa)a[fa]最小的值,其中fa是node的父亲。右子树同理,用g[node][1]维护。g数组的维护也是O(nlog(n))的,并且维护出来之后就能枚举初始点亮的节点统计答案了。


CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;

#define P pair<int,long long>
#define MP(x,y) make_pair(x,(long long)y)

const int maxn=200100;
const long long oo=1e17;
typedef long long LL;

vector <P> f[maxn];
LL g[maxn][2];

int a[maxn];
int b[maxn];
int n;

LL Len(int x,int y)
{
    if (x==y) return 0;
    if (x<y) swap(x,y);
    return Len(x>>1,y)+b[x];
}

int main()
{
    freopen("D.in","r",stdin);
    freopen("D.out","w",stdout);

    scanf("%d",&n);
    for (int i=1; i<=n; i++) scanf("%d",&a[i]);
    for (int i=2; i<=n; i++) scanf("%d",&b[i]);

    for (int i=n; i>=1; i--)
    {
        int x=i<<1,y=x|1;
        if (x>n) f[i].push_back( MP(i,0) );
        if (x==n) f[i].push_back( MP(x, Len(i,x)*a[x] ) );
        if (x<n)
        {
            LL temp1=oo,temp2=oo;
            for (int j=0; j<f[x].size(); j++)
                temp1=min(temp1, f[x][j].second+Len(f[x][j].first,y)*a[y] );
            for (int j=0; j<f[y].size(); j++)
                temp2=min(temp2, f[y][j].second+Len(f[y][j].first,x)*a[x] );
            temp1+=Len(i,x)*a[x];
            temp2+=Len(i,y)*a[y];

            for (int j=0; j<f[y].size(); j++)
                f[i].push_back( MP(f[y][j].first,f[y][j].second+temp1) );
            for (int j=0; j<f[x].size(); j++)
                f[i].push_back( MP(f[x][j].first,f[x][j].second+temp2) );
        }
    }

    for (int i=n; i>=1; i--)
    {
        g[i][0]=g[i][1]=oo;
        int x=i<<1,y=x|1,fa=i>>1;
        if (x==n)
            g[i][0]=Len(i,x)*a[x]+Len(x,fa)*a[fa],g[i][1]=Len(i,fa)*a[fa];
        if (x<n)
        {
            for (int j=0; j<f[x].size(); j++)
            g[i][0]=min(g[i][0],f[x][j].second+Len(f[x][j].first,fa)*a[fa]);
            for (int j=0; j<f[y].size(); j++)
            g[i][1]=min(g[i][1],f[y][j].second+Len(f[y][j].first,fa)*a[fa]);
            g[i][0]+=Len(i,x)*a[x];
            g[i][1]+=Len(i,y)*a[y];
        }
    }

    LL ans=oo;
    for (int i=n; i>=1; i--)
    {
        int j=i>>1,x=i;
        LL temp=oo;
        for (int k=0; k<f[i].size(); k++)
            temp=min(temp,f[i][k].second+Len(f[i][k].first,j)*a[j]);
        while (j)
        {
            temp+=g[j][(x&1)^1];
            x=j,j>>=1;
        }
        ans=min(ans,temp);
    }
    printf("%lld\n",ans);

    return 0;
}
发布了160 篇原创文章 · 获赞 76 · 访问量 10万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览