bzoj1495 [NOI2006]网络收费

http://www.elijahqi.win/2018/02/19/bzoj1495/
Description
网络已经成为当今世界不可或缺的一部分。每天都有数以亿计的人使用网络进行学习、科研、娱乐等活动。然而,
不可忽视的一点就是网络本身有着庞大的运行费用。所以,向使用网络的人进行适当的收费是必须的,也是合理的
。MY市NS中学就有着这样一个教育网络。网络中的用户一共有2N个,编号依次为1, 2, 3, …, 2N。这些用户之间
是用路由点和网线组成的。用户、路由点与网线共同构成一个满二叉树结构。树中的每一个叶子结点都是一个用户
,每一个非叶子结点(灰色)都是一个路由点,而每一条边都是一条网线(见下图,用户结点中的数字为其编号)
这里写图片描述
MY网络公司的网络收费方式比较奇特,称为“配对收费”。即对于每两个用户i, j (1≤i < j ≤2N ) 进行收费。
由于用户可以自行选择两种付费方式A、B中的一种,所以网络公司向学校收取的费用与每一位用户的付费方式有关
。该费用等于每两位不同用户配对产生费用之和。 为了描述方便,首先定义这棵网络树上的一些概念: 祖先:根
结点没有祖先,非根结点的祖先包括它的父亲以及它的父亲的祖先; 管辖叶结点:叶结点本身不管辖任何叶结点
,非叶结点管辖它的左儿子所管辖的叶结点与它的右儿子所管辖的叶结点; 距离:在树上连接两个点之间的用边
最少的路径所含的边数。 对于任两个用户i, j (1≤i)
这里写图片描述
由于最终所付费用与付费方式有关,所以NS中学的用户希望能够自行改变自己的付费方式以减少总付费。然而,由
于网络公司已经将每个用户注册时所选择的付费方式记录在案,所以对于用户i,如果他/她想改变付费方式(由A
改为B或由B改为A),就必须支付Ci元给网络公司以修改档案(修改付费方式记录)。 现在的问题是,给定每个用
户注册时所选择的付费方式以及Ci,试求这些用户应该如何选择自己的付费方式以使得NS中学支付给网络公司的总
费用最少(更改付费方式费用+配对收费的费用)。
Input
输入文件中第一行有一个正整数N。 第二行有2N个整数,依次表示1号,2号,…,2N号用户注册时的付费方式,每
一个数字若为0,则表示对应用户的初始付费方式为A,否则该数字为1,表示付费方式为B。 第三行有2N个整数,
表示每一个用户修改付费方式需要支付的费用,依次为C1, C2, …,CM 。( M=2N ) 以下2N-1行描述给定的两两用
户之间的流量表F,总第(i + 3)行第j列的整数为Fi, j+i 。(1≤i<2N,1≤j≤2N ? i) 所有变量的含义可以参
见题目描述。N≤10,0≤Fi, j≤500,0≤Ci≤500 000

Output

你的程序只需要向输出文件输出一个整数,表示NS中学支付给网络公司的最小总费用。(单位:元)
Sample Input
2
1 0 1 0
2 2 10 9
10 1 2
2 1
3
Sample Output
8
HINT

【样例说明】 将1号用户的付费方式由B改为A,NS中学支付给网络公司的费用达到最小

我真的太辣鸡了 题目读了很久 想了很久 然后不会 然后题解看了很久 代码写了很久 菜死

题意:每个用户都是配对收费 所有用户都在叶子节点上 然后两两都会产生一个花费 这个花费是什么呢 找一下他们的lca 然后 如果这个lca包含的子树 (默认1为黑色 0为白色 分别对应原题中的B方案和A方案 关于每个点的贡献都可以拆开分开来看不必两个配对起来看 假设找到了lca 然后lca这个子树里叶子节点是黑色的数量如果多于白色那么 则不花费费用 反之则花费流量w[i][j]的费用 那么好 首先先预处理v[i][j]表示当前第i个叶子节点 他对lca为j号点时的贡献加上之前观察到 我这个贡献可以拆开来看 然后呢我们必须知道每个lca是黑点多还是白点多才可以确定一些东西 那么不妨直接dfs一下 2^n钦定一下每个lca是什么状态 然后我就获得了一个状态 然后这时候再根据这个状态去进行树形背包dp 注意转移的时候只保留合法状态即可 每次做到叶子节点的时候log时间上去把我在每个lca的贡献加进去即可 因为我还可以修改方案 所以背包dp搞一下即可

复杂度o(2^n*n)可过

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define N 1100
#define l x<<1
#define r x<<1|1
int n,m,c[N];bool t[N],op[N];
ll dp[N<<1][N],v[N][N],w[N][N];
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
    return x*f;
}
inline void init(int x,int d){
    if (!d) return;init(l,d-1);init(r,d-1);
    for (int i=x<<d;i<(r)<<d-1;++i)
        for (int j=(r)<<d-1;j<x+1<<d;++j) v[i-m][x]+=w[i-m][j-m],v[j-m][x]+=w[i-m][j-m];
}
inline void dfs(int x,int d){
    if (!d){
        dp[x][op[x-m]^1]=c[x-m];dp[x][op[x-m]]=0;
        for (int i=x>>1;i;i>>=1) dp[x][t[i]]+=v[x-m][i];return;
    }memset(dp[x],0x3f,sizeof(ll)*((1<<d)+1));
    t[x]=1;//white is more than black
    dfs(l,d-1);dfs(r,d-1);
    for (int i=0;i<=1<<d-1;++i)
        for (int j=0;j<=(1<<d-1)-i;++j) dp[x][i+j]=min(dp[x][i+j],dp[l][i]+dp[r][j]);
    t[x]=0;//black is more than white
    dfs(l,d-1);dfs(r,d-1);
    for (int i=1;i<=1<<d-1;++i)
        for (int j=(1<<d-1)-i+1;j<=1<<d-1;++j) dp[x][i+j]=min(dp[x][i+j],dp[l][i]+dp[r][j]);
}
int main(){
    //freopen("bzoj1495.in","r",stdin);     
    n=read();m=1<<n;for (int i=0;i<m;++i) op[i]=read();
    for (int i=0;i<m;++i) c[i]=read();ll ans=1LL<<60;
    for (int i=0;i<m;++i) for (int j=i+1;j<m;++j) w[i][j]=read();
    init(1,n);/*for (int i=0;i<m;++i) {
        for (int j=1;j<m;++j) printf("%d ",v[i][j]);puts("");
    }*/
    dfs(1,n);for (int i=0;i<=m;++i) ans=min(ans,dp[1][i]);
    printf("%lld",ans);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值