网络收费
点击标题链接查看题目.
最初想法
类似线段树建出一颗树, 暴力枚举每个节点改不改 .
正解部分
观察题目的表格, 可以发现:
- 满足 Na<Nb 的子树上, 只有 A 收费方式需要付费 .
- 满足 Na≥Nb 的子树上, 只有 B 收费方式需要付费 .
但是此题为两两配对产生贡献,
直接计算 时间复杂度为 O(N2), 考虑将两个点拆开一个一个去计算 .
每个 非叶节点 都表示了若干点对的 LCA, 结合上方观察得出的结论, 每个节点对答案造成的贡献都可看作在 非叶节点 造成的 .
则设 cost[i,j] 表示 i 这个 叶节点, 在深度为 j 的 祖先(非叶节点) 中与若干点配对产生的 总流量(费用), 可以 O(N2) 预处理.
设 F[i,j] 表示 以 i 号节点为根的子树, 管辖的叶子节点中含有 j 个 B 收费方式叶子节点 的 最小花费,
则
F[k,i+j]=min(F[left_soni,i]+F[right_soni,j]) cntb∈[0,sizei]
- 若 Na<Nb 需要满足条件 sizei−i−j<i+j
- 若 Na≥Nb 需要满足条件 sizei−i−j≥i+j
这样往下递归, 每次假定当前节点 Na<Nb 还是 Na≥Nb.
到了叶子节点就可以直接计算这个叶子节点产生的贡献了, 具体地说:
设当前递归到了 叶子节点 a,
- 若当前为 A 付费方式, 对答案的贡献为 F[a,0]=i=0∑max_dep!tmp[i]∗cost[a,i] .
- 若当前为 B 付费方式, 对答案的贡献为 F[a,1]=i=0∑max_deptmp[i]∗cost[a,i] .
在上方的 答案统计中, 当且仅当 目前收费方式与 初始收费方式 不同时, 需要加上修改产生的花费 .
实现部分
对收费方式的处理: 使用 C[0/1][i] 表示第 i 个用户使用 A/B 收费方式时的 修改 费用, 递归到叶子节点时赋初值 可以方便地处理 修改产生的费用 .
- tmp[dep]=0 表示当前 DFS链 中深度为 dep 的 当前节点 以下 Na<Nb, 只有 A 付费.
- tmp[dep]=1 则表示 Na≥Nb, 只有 B 付费 .
求解 LCA 深度时, 可以不断 “试” 两个待测节点 x,y, 具体地说:
不断的对 x,y 执行 >>i 操作, 相当于向上走 i 步, 当它们走到的节点不同时, 那两个不同的节点的 父亲 即为 Lca, 深度为 max_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\n", Ans);
return 0;
}