P4297 [NOI2006]网络收费 [树形dp]

网络收费

点击标题链接查看题目.


color{blue}{最初想法}

类似线段树建出一颗树, 暴力枚举每个节点改不改 .


color{red}{正解部分}

观察题目的表格, 可以发现:

  • 满足 Na<NbNa < Nb 的子树上, 只有 AA 收费方式需要付费 .
  • 满足 NaNbNa geq Nb 的子树上, 只有 BB 收费方式需要付费 .

但是此题为两两配对产生贡献,
直接计算 时间复杂度为 O(N2)O(N^2), 考虑将两个点拆开一个一个去计算 .
每个 非叶节点 都表示了若干点对的 LCALCA, 结合上方观察得出的结论, 每个节点对答案造成的贡献都可看作在 非叶节点 造成的 .
则设 cost[i,j]cost[i, j] 表示 ii 这个 叶节点, 在深度为 jj祖先(非叶节点) 中与若干点配对产生的 总流量(费用), 可以 O(N2)O(N^2) 预处理.

F[i,j]F[i, j] 表示 以 ii 号节点为根的子树, 管辖的叶子节点中含有 jjBB 收费方式叶子节点 的 最小花费,

F[k,i+j]=min(F[left_soni,i]+F[right_soni,j])       cntb[0,sizei]F[k, i+j]=min(F[left\_son_i, i]+F[right\_son_i,j]) cnt_b∈[0,size_i]

  • Na<NbNa < Nb 需要满足条件 sizeiij<i+jsize_i-i-j<i+j
  • NaNbNa geq Nb 需要满足条件 sizeiiji+jsize_i-i-j geq i+j

这样往下递归, 每次 假定 当前节点 Na<NbNa<Nb 还是 NaNbNa geq Nb.

到了叶子节点就可以直接计算这个叶子节点产生的贡献了, 具体地说:

设当前递归到了 叶子节点 aa,

  • 若当前为 AA 付费方式, 对答案的贡献为 F[a,0]=i=0max_dep!tmp[i]cost[a,i]F[a,0]=sumlimits_{i=0}^{max\_dep} !tmp[i]*cost[a, i] .
  • 若当前为 BB 付费方式, 对答案的贡献为 F[a,1]=i=0max_deptmp[i]cost[a,i]F[a,1]=sumlimits_{i=0}^{max\_dep}tmp[i]*cost[a, i] .

在上方的 答案统计中, 当且仅当 目前收费方式与 初始收费方式 不同时, 需要加上修改产生的花费 .


color{red}{实现部分}

对收费方式的处理: 使用 C[0/1][i]C[0/1][i] 表示第 ii 个用户使用 A/BA/B 收费方式时的 修改 费用, 递归到叶子节点时赋初值 可以方便地处理 修改产生的费用 .

  • tmp[dep]=0tmp[dep] = 0 表示当前 DFSDFS链 中深度为 depdep当前节点 以下 Na<NbNa<Nb, 只有 AA 付费.
  • tmp[dep]=1tmp[dep] = 1 则表示 NaNbNa geq Nb, 只有 BB 付费 .

求解 LCALCA 深度时, 可以不断 “试” 两个待测节点 x,yx, y, 具体地说:
不断的对 x,yx, y 执行 >>i>>i 操作, 相当于向上走 ii 步, 当它们走到的节点不同时, 那两个不同的节点的 父亲 即为 LcaLca, 深度为 max_depi1max\_dep-i-1 .


#include<bits/stdc++.h>
#define reg register
typedef long long ll;

const int maxn = 3500;

int n;
int N;
int tmp[13];
int Fs[maxn];
ll C[2][maxn];
ll F[maxn][maxn];
ll cost[maxn][13];

int Lca(int x, int y){
        for(reg int i = n; i >= 0; i --)
        	if((x>>i) != (y>>i)) return n-i-1;
}

void DFS(int k, int dep, int l, int r){
        if(dep == n){
                F[k][0] = C[0][l], F[k][1] = C[1][l];
                for(reg int i = 0; i < dep; i ++) F[k][tmp[i]] += cost[l][i];
                return ;
        }

        int mid = l+r >> 1;

	memset(F[k], 0x3f, sizeof F[k]);

        tmp[dep] = 0;
        DFS(k<<1, dep+1, l, mid), DFS(k<<1|1, dep+1, mid+1, r);
        for(reg int i = 0; i <= mid-l+1; i ++)
                for(reg int j = 0; j <= mid-l+1; j ++){
                	if(r-l+1-i-j >= i+j) continue ;
			F[k][i+j] = std::min(F[k][i+j], F[k<<1][i] + F[k<<1|1][j]);
		}

        tmp[dep] = 1;
        DFS(k<<1, dep+1, l, mid), DFS(k<<1|1, dep+1, mid+1, r);
        for(reg int i = 0; i <= mid-l+1; i ++)
                for(reg int j = 0; j <= mid-l+1; j ++){
                	if(r-l+1-i-j < i+j) continue ;
			F[k][i+j] = std::min(F[k][i+j], F[k<<1][i] + F[k<<1|1][j]);
		}
}

int main(){
        scanf("%d", &n); N = 1 << n;
        for(reg int i = 1; i <= N; i ++) scanf("%d", &Fs[i]);
        for(reg int i = 1; i <= N; i ++) scanf("%lld", &C[!Fs[i]][i]), C[Fs[i]][i] = 0;
        for(reg int i = 1; i <= N; i ++)
                for(reg int j = i+1; j <= N; j ++){
                        int x; scanf("%d", &x);
                        int lca_dep = Lca(i-1, j-1);
                        cost[i][lca_dep] += x, cost[j][lca_dep] += x;
                }
        DFS(1, 0, 1, N);
        ll Ans = 1e18;
        for(reg int i = 0; i <= N; i ++) Ans = std::min(Ans, F[1][i]);
        printf("%lld
", Ans);
        return 0;
}

原文地址:https://www.cnblogs.com/zbr162/p/11822541.html