$40\ pts$

完全是送的。

直接枚举需要修改的集合然后算贡献,复杂度大概是 $O(2^{2^n})$ 级别。

$100\ pts$

发现自己好蠢啊......

设 $f_{i,j}$ 表示树上第 $i$ 个点其子树下的叶子节点中有 $j$ 个 $\rm{B}$ 类结点,其他的全部是 $\rm{A}$ 类结点时,关于这棵子树的最小费用。

转移如下:

$$ f_{i,j}=\min\{f_{lson_i,x}+f_{rson_i,y}\ \ [x+y=j]\} $$

考虑初始化,如果当前点是叶子节点的话,对于一个其他的叶子节点 $j$ ,它们所产生的 $f_{i,j}$ 的贡献只会在 $lca_{i,j}$ 上计算。

现在给 $lca$ 也赋一个值,如果 $nA<nB$ 值为 $B$ ,否则为 $A$ 。这样 $i,j$ 两点的贡献便可以分开来算。如果 $i$ 和 $lca_{i,j}$ 的值不同,那么 $lca_{i,j}$ 的费用将加上一个 $f_{i,j}$ ,同时 $j$ 也一样。

那么赋初值也搞定了,代码:

if(dep==n) {/*当x已经是叶子节点时*/
    dp[x][0]=C[x-m][0],
    dp[x][1]=C[x-m][1];
    /*C[x][0]就是点x选择0付费方式时需支付的额外费用*/
    /*就像题目中给定的转换费用*/
    for(int i=dep;i>=0;--i) dp[x][vis[i]^1]+=f[x-m][i];
    /*输入的时候就将所有的贡献记到lca上,这个时候用就好*/
    /*i是按照深度枚举祖先,vis[i]是当前的祖先钦定的值(0/1)*/
    return;
}

树形 $\rm{DP}$ ,$dfs$ 的时候对于一个点 $x$ ,我们钦定其的值为 $A$ 或者是 $B$ ,转移的时候对 $x+y$ 的大小判断一下即可。

Code:

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N=1024+5;
int n,m,vis[N],t[N];
ll C[N][2],f[N][N],dp[N][N];

template <typename _Tp> inline void IN(_Tp&x) {
    char ch;bool flag=0;x=0;
    while(ch=getchar(),!isdigit(ch)) if(ch=='-') flag=1;
    while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    if(flag) x=-x;
}

inline int lca(int x,int y) {
    for(int i=n-1;i>=0;--i)
        if((x>>i)^(y>>i)) return n-i-1;
}
void dfs(int x,int dep,int l,int r) {
    if(dep==n) {
        dp[x][0]=C[x-m][0],dp[x][1]=C[x-m][1];
        for(int i=dep;i>=0;--i) dp[x][vis[i]^1]+=f[x-m][i];
        return;
    }
    memset(dp[x],0x3f,sizeof(dp[x]));
    int mid=(l+r)>>1;
    vis[dep]=0;
    dfs(x<<1,dep+1,l,mid),dfs(x<<1|1,dep+1,mid+1,r);
    for(int i=0;i<=mid-l+1;++i)
        for(int j=0;j<=r-mid&&i+j<=r-l+1;++j)
            if(i+j<=r-l+1-i-j) dp[x][i+j]=min(dp[x][i+j],dp[x<<1][i]+dp[x<<1|1][j]);
    /*上面的if就是确保当前值下计算的答案符合要求*/
    vis[dep]=1;
    dfs(x<<1,dep+1,l,mid),dfs(x<<1|1,dep+1,mid+1,r);
    for(int i=0;i<=mid-l+1;++i)
        for(int j=0;j<=r-mid&&i+j<=r-l+1;++j)
            if(i+j>r-l+1-i-j) dp[x][i+j]=min(dp[x][i+j],dp[x<<1][i]+dp[x<<1|1][j]);
}

int main() {
    IN(n),m=1<<n;
    for(int i=0;i<m;++i) IN(t[i]);
    for(int i=0;i<m;++i) IN(C[i][t[i]^1]);
    for(int i=0;i<m;++i) 
        for(int j=i+1,v;j<m;++j)
            IN(v),f[i][lca(i,j)]+=v,f[j][lca(i,j)]+=v;
    dfs(1,0,1,m);
    ll ans=1e18+2;
    for(int i=0;i<=m;++i) ans=min(ans,dp[1][i]);
    printf("%lld\n",ans);
    return 0;
}
最后修改:2019 年 07 月 28 日 12 : 47 PM