bzoj4711-小奇挖矿

Posted ubospica

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4711-小奇挖矿相关的知识,希望对你有一定的参考价值。

Description

【题目背景】

小奇在喵星系使用了无限非概率驱动的采矿机,以至于在所有星球上都采出了一些矿石,现在它准备建一些矿石仓库并把矿石运到各个仓库里。

【问题描述】

喵星系有n个星球,标号为1到n,星球以及星球间的航线形成一棵树。所有星球间的双向航线的长度都为1。小奇要在若干个星球建矿石仓库,设立每个仓库的费用为K。对于未设立矿石仓库的星球,设其到一个仓库的距离为i,则将矿石运回的费用为Di。请你帮它决策最小化费用。

输入

第一行2个整数n,K。

第二行n-1个整数,D1,D2,…Dn-1,保证Di<=Di+1。

接下来n-1行,每行2个整数x,y,表示星球x和星球y存在双向航线。

n<=200,0<=K,Di<=100000

输出

输出一行一个整数,表示最小费用。

样例输入

8 10
2 5 9 11 15 19 20
1 4
1 3
1 7
4 6
2 8
2 3
3 5

样例输出

38

【样例解释】

在1,2号星球建立仓库。

Solution

似乎和std并不一样... 好像还更快?

下文中 "连向" 表示运向某个仓库.

容易发现一个点 \(p\) 的儿子 \(v\) 连向的点要么与 \(p\) 连向的点相同, 要么在 \(v\) 的子树内.

\(f_{i,j \in \left\{-n,n\right\} }\) 表示\(i\) 点连向距离 \(|j|\) 的点, \(i\) 子树内点的运输/建立仓库的总费用的最小值, 并且 \(i\)
\[ \begin{cases} 连向父亲 & (j>0) \自己为仓库 & (j=0) \连向某个儿子 & (j<0) \end{cases} \]
.

考虑转移: 令 \(d > 0\), \(v\)\(i\) 的儿子,
\[ \begin{cases} f_{i,-d} = d_d + \min_{v_0 \in \{v\}}\{ f_{v_0,-(d-1)} + \sum_{v \not= v0} (\min \{f_{v,d+1},f_{v,\le 0}\} )\} \f_{i,0} = K + \sum_{v} (\min \{f_{v,1},f_{v,\le 0}\}) \f_{i,d} = d_d + \sum_{v} (\min \{f_{v,d+1},f_{v,\le 0}\} ) \end{cases} \]
边界条件挺复杂的... 看代码吧...

然后这个东西可以前缀min优化, 时间复杂度 \(\operatorname O (n^2)\)

愉快的拿了目前rk1... (虽然是在本校OJ评测的)

Code

#pragma GCC diagnostic error "-std=c++11"
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
using namespace std;
#define rep(i,l,r) for(register int i=(l);i<=(r);++i)
#define repdo(i,l,r) for(register int i=(l);i>=(r);--i)
#define il inline
typedef double db;
typedef long long ll;

//--------------------------------------
const int nsz=250,ninf=1e9;
int n,k,d[nsz];

vector<int> edge[nsz];
void adde(int f,int t){edge[f].push_back(t),edge[t].push_back(f);}

int dp[nsz][nsz*2],minv[nsz],maxd[nsz];
void dfs(int p,int f){
    for(vector<int>::iterator i=edge[p].begin();i!=edge[p].end();){
        int v=*i;
        if(v!=f)dfs(v,p),maxd[p]=max(maxd[p],maxd[v]+1),++i;
        else i=edge[p].erase(i);
    }
    rep(i,1,maxd[p]){
        dp[p][-i+n]=d[i];
        if(i==n-1){
            dp[p][-i+n]+=dp[edge[p].front()][-(i-1)+n];
            continue;
        }
        int a=0,b=ninf,tmp;
        for(int v:edge[p]){
            int tmp=min(dp[v][i+1+n],minv[v]);
            a+=tmp;
            if(maxd[v]>=i-1)b=min(b,dp[v][-(i-1)+n]-tmp);
        }
        dp[p][-i+n]+=a+b;
    }
    rep(i,0,n-1){
        dp[p][i+n]=d[i];
        if(i==n-1){
            for(int v:edge[p])dp[p][i+n]+=minv[v];
            continue;
        }
        for(int v:edge[p])dp[p][i+n]+=min(dp[v][i+1+n],minv[v]);
    }
    minv[p]=ninf;
    rep(i,-maxd[p],0)minv[p]=min(minv[p],dp[p][i+n]);
}

int main(){
    ios::sync_with_stdio(0),cin.tie(0);
    cin>>n>>k;
    rep(i,1,n-1)cin>>d[i];
    d[0]=k;
    int a,b;
    rep(i,1,n-1)cin>>a>>b,adde(a,b);
    dfs(1,0);
    cout<<minv[1]<<'\n';
    return 0;
}

以上是关于bzoj4711-小奇挖矿的主要内容,如果未能解决你的问题,请参考以下文章

bzoj4711-小奇挖矿

4711: 小奇挖矿

NOIP模拟赛小奇挖矿 2

2019.10.16&17小结

8.24考试总结

BZOJ4550小奇的博弈 博弈论