BZOJ_1495_[NOI2006]网络收费_树形DP

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


直接模拟退火有80分。

代码:

// luogu-judger-enable-o2
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef double f2;
#define N 1050
int f[N][N],C[N],n,m,cnt,ls[N<<2],rs[N<<2],a[N],fa[N<<2],L[N][N],R[N][N];
int c1[N],c2[N],b[N];
struct A {
int l,r;
}t[N<<2];
void build(int l,int r,int &p) {
if(l==r) {p=l; return ;}
else p=++cnt;
t[p]=(A){l,r};
int mid=(l+r)>>1;
build(l,mid,ls[p]); build(mid+1,r,rs[p]);
fa[ls[p]]=p; fa[rs[p]]=p;
}
A lca(int x,int y) {
while(x!=y) {
x=fa[x]; y=fa[y];
}
return t[x];
}
struct Q {
int b[N];
}ans;
int mn=1<<30;
int get_cost(const Q tmp) {
int i,re=0,j;
for(i=1;i<=m;i++) b[i]=tmp.b[i];
for(i=1;i<=m;i++) {
if(a[i]!=b[i]) re+=C[i];
if(!b[i]) c1[i]=c1[i-1]+1,c2[i]=c2[i-1];
else c1[i]=c1[i-1],c2[i]=c2[i-1]+1;
}
for(i=1;i<=m;i++) {
for(j=i+1;j<=m;j++) {
int l=L[i][j],r=R[i][j],na=c1[r]-c1[l-1],nb=c2[r]-c2[l-1];
if(!b[i]&&!b[j]) {
if(na<nb) re+=2*f[i][j];
}else if(!b[i]&&b[j]) {
re+=f[i][j];
}else if(b[i]&&!b[j]) {
re+=f[i][j];
}else {
if(na>=nb) re+=2*f[i][j];
}
}
}
if(mn>re) {
mn=re; ans=tmp;
}
return re;
}
f2 Rand() {
return 1.0*rand()/RAND_MAX;
}
void orz(f2 BG,f2 ED,f2 d) {
f2 B=BG;
int i,tmn;
Q nowp;
for(i=1;i<=m;i++) nowp.b[i]=a[i];
tmn=get_cost(nowp);
for(i=1;i<=m;i++) b[i]=a[i];
for(;B>ED;B*=d) {
Q tmp=nowp;
for(i=1;i<=B;i++) {
int k=rand()%m+1;
tmp.b[k]^=1;
}
int tans=get_cost(tmp);
if(tans<tmn||Rand()<exp(1.0*(tmn-tans)/B)) {
tmn=tans; nowp=tmp;
}
}
for(i=1;i<=1000;i++) {
Q tmp=ans;
int k=rand()%m+1;
tmp.b[k]^=1;
get_cost(tmp);
}
}
int main() {
// freopen("network.in","r",stdin);
// freopen("network.out","w",stdout);
srand(19260817); rand();
scanf("%d",&n);
m=1<<n; cnt=1<<n;
int i,j;
for(i=1;i<=m;i++) scanf("%d",&a[i]);
for(i=1;i<=m;i++) scanf("%d",&C[i]);
for(i=1;i<=m;i++) for(j=i+1;j<=m;j++) scanf("%d",&f[i][j]);
int root=n;
build(1,m,root);
for(i=1;i<=m;i++) {
for(j=i+1;j<=m;j++) {
A tmp=lca(i,j); L[i][j]=tmp.l; R[i][j]=tmp.r;
}
}
orz(m,1.5,0.99);
printf("%d\n",mn);
}
/*
2
1 0 1 0
2 2 10 9
10 1 2
2 1
3
*/

观察计算贡献的方式,相当于选哪边的少就计算哪边的贡献,此时i的贡献不止有小于i的这部分,还有大于i的这部分。

这样,只需要知道每个点对应子树内选哪边的少就可以确定贡献的计算方法。

设s[i][j]表示第i个点对应的深度为j的祖先上有多少贡献,这步在求lca的时候处理。

然后dfs整棵树。对于每个节点,枚举选a多还是选b多,在叶子节点统计答案。

时间复杂度:O(2^n *n)

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 2050
#define ls p<<1
#define rs p<<1|1
#define _min(x,y) ((x)<(y)?(x):(y))
__attribute__((optimize("-O3")))inline char nc() {
static char buf[100000],*p1,*p2;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
__attribute__((optimize("-O3")))int rd() {
int x=0; char s=nc();
while(s<'0'||s>'9') s=nc();
while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+s-'0',s=nc();
return x;
}
int f[N][1050],n,C[N],s[N][15],m,sta[N],a[N],g[4][N];
struct A {
int l,r,dep;
}t[N];
__attribute__((optimize("-O3")))void build(int l,int r,int p) {
t[p].l=l; t[p].r=r;
if(l==r) return ;
int mid=(l+r)>>1;
t[ls].dep=t[rs].dep=t[p].dep+1;
build(l,mid,ls); build(mid+1,r,rs); g[0][ls]=g[0][rs]=p;
}
__attribute__((optimize("-O3")))int lca(int x,int y) {
int i;
if(g[3][x]!=g[3][y]) x=g[3][x],y=g[3][y];
if(g[2][x]!=g[2][y]) x=g[2][x],y=g[2][y];
if(g[1][x]!=g[1][y]) x=g[1][x],y=g[1][y];
if(g[0][x]!=g[0][y]) x=g[0][x],y=g[0][y];
return g[0][x];
}
__attribute__((optimize("-O3")))void dfs(int p) {
int d=t[p].dep,i,j;
if(d==n) {
if(a[p-m+1]) f[p][1]=0,f[p][0]=C[p-m+1];
else f[p][0]=0,f[p][1]=C[p-m+1];
for(i=0;i<n;i++) f[p][!sta[i]]+=s[p-m+1][i];
return ;
}
int y=1<<(n-d-1);
for(i=0;i<=y<<1;i++) f[p][i]=1<<30;
sta[d]=0;
dfs(ls); dfs(rs);
for(i=0;i<=y;i++) for(j=0;i+j<=y;j++) f[p][i+j]=_min(f[p][i+j],f[ls][i]+f[rs][j]);
sta[d]=1;
dfs(ls); dfs(rs);
for(i=1;i<=y;i++) for(j=y-i+1;j<=y;j++) f[p][i+j]=_min(f[p][i+j],f[ls][i]+f[rs][j]);
}
__attribute__((optimize("-O3")))int main() {
n=rd();
m=1<<n;
build(1,m,1);
int i,j,x;
int k=m<<1;
for(i=1;i<=3;i++) for(j=1;j<=k;j++) g[i][j]=g[i-1][g[i-1][j]];
for(i=1;i<=m;i++) a[i]=rd();
for(i=1;i<=m;i++) C[i]=rd();
for(i=1;i<=m;i++) {
for(j=i+1;j<=m;j++) {
int l=lca(i+m-1,j+m-1);
x=rd();
s[i][t[l].dep]+=x;
s[j][t[l].dep]+=x;
}
}
// puts("FUCK");
dfs(1);
int ans=1<<30;
for(i=0;i<=m;i++) ans=_min(ans,f[1][i]);
printf("%d\n",ans);
}

BZOJ_1495_[NOI2006]网络收费_树形DP的更多相关文章

  1. BZOJ1495 [NOI2006]网络收费 【树形dp + 状压dp】

    题目链接 BZOJ1495 题解 观察表格,实际上就是分\(A\)多和\(B\)两种情况,分别对应每个点选\(A\)权值或者\(B\)权值,所以成对的权值可以分到每个点上 所以每个非叶节点实际对应一个 ...

  2. 【bzoj1495】[NOI2006]网络收费 暴力+树形背包dp

    题目描述 给出一个有 $2^n$ 个叶子节点的完全二叉树.每个叶子节点可以选择黑白两种颜色. 对于每个非叶子节点左子树中的叶子节点 $i$ 和右子树中的叶子节点 $j$ :如果 $i$ 和 $j$ 的 ...

  3. BZOJ 1495 [NOI2006]网络收费(暴力DP)

    题意 给定一棵满二叉树,每个叶节点有一个状态0/10/10/1,对于每两个叶节点i,ji,ji,j,如果这两个叶节点状态相同但他们的LCALCALCA所管辖的子树中的与他们状态相同的叶节点个数较少(少 ...

  4. 【BZOJ1495】[NOI2006]网络收费 暴力+DP

    [BZOJ1495][NOI2006]网络收费 Description 网络已经成为当今世界不可或缺的一部分.每天都有数以亿计的人使用网络进行学习.科研.娱乐等活动.然而,不可忽视的一点就是网络本身有 ...

  5. 洛谷 P4297 [NOI2006]网络收费

    P4297 [NOI2006]网络收费 题目背景 noi2006 day1t1 题目描述 网络已经成为当今世界不可或缺的一部分.每天都有数以亿计的人使用网络进行学习.科研.娱乐等活动.然而,不可忽视的 ...

  6. 并不对劲的[noi2006]网络收费

    题目略长,就从大视野上复制了. 听上去好像费用流,然而…… ***************************表示略长的题目的分界线************************ 1495: [ ...

  7. 【简】题解 P4297 [NOI2006]网络收费

    传送门:P4297 [NOI2006]网络收费 题目大意: 给定一棵满二叉树,每个叶节点有一个状态(0,1),任选两个叶节点,如果这两个叶节点状态相同但他们的LCA所管辖的子树中的与他们状态相同的叶节 ...

  8. BZOJ_1864_[Zjoi2006]三色二叉树_树形DP

    BZOJ_1864_[Zjoi2006]三色二叉树_树形DP 题意: 分析:递归建树,然后DP,从子节点转移. 注意到红色和蓝色没有区别,因为我们可以将红蓝互换而方案是相同的.这样的话我们只需要知道当 ...

  9. BZOJ_3573_[Hnoi2014]米特运输_树形DP+hash

    BZOJ_3573_[Hnoi2014]米特运输_树形DP+hash 题意: 给你一棵树每个点有一个权值,要求修改最少的权值,使得每个节点的权值等于其儿子的权值和且儿子的权值都相等. 分析: 首先我们 ...

随机推荐

  1. Android 自定义录音、播放动画View,让你的录音浪起来

    最近公司项目有一个录音的录制和播放动画需求,然后时间是那么紧,那么赶紧开撸. 先看效果图 嗯,然后大致就是这样,按住录音,然后有一个倒计时,最外层一个进度条,还有一个类似模拟声波的动画效果(其实中间的 ...

  2. DIV浮动IE文本产生3象素的bug

    描写叙述:DIV浮动IE文本产生3象素的bug    左边对象浮动.右边採用外补丁的左边距来定位,右边对象(div)会离左边有3px的间距 复现:在开发者工具里把文本模式设置了杂项后会出现3像素的bu ...

  3. java数据库连接池简单实现

    package cn.lmj.utils; import java.io.PrintWriter; import java.lang.reflect.InvocationHandler; import ...

  4. 安装mongoDB遇见的一个路径问题

    如果安装路径不存在,则不会解压EXE软件! 安装monogoDB后,它不会自动添加执行路径! 意思就是安装路径是D盘下面的mongoDB文件夹,假如不存在这个文件夹,则不会安装成功 你需要添加路径: ...

  5. FFmpeg for ios架构:中级

    FFmpeg这部分想了非常久,也没找到比較好的解说方式.本来想像其他博客一样.对着代码一行行的分析.但后来感觉不太现实,FFmpeg应用在IOS上怎么说代码最少也有个5.6k行(包含音视频.业务逻辑) ...

  6. 免费DNSserver有哪些?

    DNS 是上网中极其重要的一环,因为电脑仅仅认识数字组成的 IP 地址,人们发明了域名来帮助记忆 (如 iPlaySoft.com),因此,在訪问不论什么域名时.背后都须要一台 DNS server来 ...

  7. 找top 10信息

    本文章内容来源于<程序猿面试宝典>. 题目: 有1千万条短信,以文本文件的形式保存.一行一条,有反复.请用5分钟时间,找出反复出现最多的前10条. 解析: 某些面试者想用数据库的办法来实现 ...

  8. x264 编码数配置

    记录项目中用到一组x264快速编码参数配置,具体如下: param->i_frame_reference = 1; param->i_scenecut_threshold = 0; par ...

  9. 关于Yapi出现 请求异常,请检查 chrome network 错误信息...

    项目开发中由于后台接口还没有,打算使用mock模拟本地数据,配置好接口,运行接口出现 检查了cross-request插件是否安装以及激活,发现没有问题,最后发现是我的请求地址写错了,,这里请求地址需 ...

  10. 系统函数C字符串的实现(11):strchr

    字符查找函数strchr char *mystrchr(const char *str, const char c) { char *p = NULL; for (char*newp = str; * ...