bzoj 1495
这是一道...卡了我一个月的树形dp...
我真是太弱了...
其实仔细想想,这题的核心思路并不是特别复杂,但是的确存在不小的难度
作为一个看过全网基本所有题解+标程才弄明白这题到底怎么回事的蒟蒻,我努力把所有东西揉到一起让各位看官一眼看懂...
首先我们简化一下题意:给定一棵满二叉树,每个叶节点有一个状态(0,1),任选两个叶节点,如果这两个叶节点状态相同但他们的LCA所管辖的子树中的与他们状态相同的叶节点个数较少(少于1/2),则会产生2f的代价,如果状态不同,则产生f的代价,如果状态相同且LCA管辖子树中与他们状态相同叶节点个数较多,则不产生代价,现在每个节点可以变更状态,但变更状态也有自己的代价,求最小总代价
那么..怎么搞?
首先,有一个很重要的思想:点对之间的问题是难以快速解决的!
很简单,因为如果我们按点对统计贡献,那么在枚举到每个叶节点都需要考虑其他所有叶节点,这样是不利于我们处理问题的
所以我们第一步要考虑的是把点对的贡献压到一个点上
很幸运,题目给出了这样的方式:
注意题目中“产生2f的代价”“产生f的代价”这两句话!
那么如果我们同样给所有非叶节点一个状态(0,1),代表这个节点管辖的子树中叶节点状态为0的多还是状态为1的多,那么我们显然可以看到:如果一个叶节点和这个根节点的状态相同,那么这个叶节点不会产生贡献,反之会产生一个f的贡献!
证明:分类讨论:
①:假设两个节点与根节点状态都相同,那么这两个节点都不产生贡献,满足题意
②:假设两个节点与根节点状态都不同,那么这两个节点都产生一个f的贡献,满足题意
③:如果有一个点与根节点状态相同,那么这两个节点只产生一个f的贡献,同样满足题意
因此我们这样处理点对是正确的
总结:在处理点对时,我们要把一个点对的贡献压到一个点上,同时给所有非叶节点一个状态,这样就能满足题意了
接下来就可以进行树形dp了
记状态dp[i][j]表示当前位于树上的第i个节点,其子树的叶节点中状态为0的点的个数为j时所需的最小代价
(这里对题意有一个小提示:当两种状态的叶节点数量相等时,认为状态为0的叶节点个数多)
如果这个节点不是叶节点,那么显然,这个节点会有两种可能的状态:
①:这个节点状态为1,认为对应的情况为子树中状态为0的节点偏多,那么可以转移的dp部分是叶节点个数/2-叶节点个数
②:这个节点状态为0,认为对应的情况为子树中状态为1的节点偏多,那么可以转移的dp部分是0-叶节点个数/2-1
那么我们分两类dfs处理,对每类情况分别合并即可
如果这个节点是叶节点,那么我们反过来枚举他上面一条链的情况,然后统计代价即可,一定注意dp的第二维代表状态为0的节点的数目!
在统计代价的时候,我们还涉及一个小问题,就是如果每次找到根节点时都枚举所有点计算代价,时间承受不了,所以我们对代价求前缀和,然后用类似倍增的方法计算总代价即可。
当然,我们不可能一直枚举上面一条链的情况,所以我们直接把这条链的状态状压,传进dfs里即可,这样也就不涉及网上大部分题解都涉及到的压缩空间的问题了。
提示:这是一棵满二叉树,所以可以用类似线段树的方式去遍历。
这样这道题就结束了
- #include <cstdio>
- #include <cmath>
- #include <cstring>
- #include <cstdlib>
- #include <iostream>
- #include <algorithm>
- #include <queue>
- #include <stack>
- #define rt1 rt<<1
- #define rt2 (rt<<1)|1
- using namespace std;
- int dp[(<<)+][(<<)+];
- int v[(<<)+][(<<)+];
- int cv[(<<)+];
- int ori[(<<)+];
- int temp[(<<)+];
- int lq[],rq[];
- int n;
- void dfs(int rt,int l,int r,int sit,int dep)
- {
- if(l==r)
- {
- dep--;
- dp[rt][]=dp[rt][]=;
- if(ori[l])
- {
- dp[rt][]=cv[l];
- }else
- {
- dp[rt][]=cv[l];
- }
- for(int i=;i<=dep;i++)
- {
- int mid=(lq[i]+rq[i])>>;
- if((sit&(<<(dep-i))))
- {
- if(l<=mid)
- {
- dp[rt][]+=v[l][rq[i]]-v[l][mid];
- }else
- {
- dp[rt][]+=v[l][mid]-v[l][lq[i]-];
- }
- }else
- {
- if(l<=mid)
- {
- dp[rt][]+=v[l][rq[i]]-v[l][mid];
- }else
- {
- dp[rt][]+=v[l][mid]-v[l][lq[i]-];
- }
- }
- }
- return;
- }
- int mid=(l+r)>>;
- int len=r-l+;
- lq[dep]=l;
- rq[dep]=r;
- dfs(rt1,l,mid,(sit<<),dep+);
- dfs(rt2,mid+,r,(sit<<),dep+);
- memset(temp,0x3f,sizeof(temp));
- for(int i=;i<=len/-;i++)
- {
- for(int j=;j<=i;j++)
- {
- temp[i]=min(temp[i],dp[rt1][j]+dp[rt2][i-j]);
- }
- }
- for(int i=;i<=len/-;i++)
- {
- dp[rt][i]=temp[i];
- }
- dfs(rt1,l,mid,(sit<<)|,dep+);
- dfs(rt2,mid+,r,(sit<<)|,dep+);
- memset(temp,0x3f,sizeof(temp));
- for(int i=len/;i<=len;i++)
- {
- for(int j=;j<=i;j++)
- {
- temp[i]=min(temp[i],dp[rt1][j]+dp[rt2][i-j]);
- }
- }
- for(int i=len/;i<=len;i++)
- {
- dp[rt][i]=temp[i];
- }
- }
- int main()
- {
- scanf("%d",&n);
- n=(<<n);
- for(int i=;i<=n;i++)
- {
- scanf("%d",&ori[i]);
- }
- for(int i=;i<=n;i++)
- {
- scanf("%d",&cv[i]);
- }
- for(int i=;i<=n;i++)
- {
- for(int j=i+;j<=n;j++)
- {
- scanf("%d",&v[i][j]);
- v[j][i]=v[i][j];
- }
- }
- for(int i=;i<=n;i++)
- {
- for(int j=;j<=n;j++)
- {
- v[i][j]=v[i][j-]+v[i][j];
- }
- }
- memset(dp,0x3f,sizeof(dp));
- dfs(,,n,,);
- int ans=;
- for(int i=;i<=n;i++)
- {
- ans=min(ans,dp[][i]);
- }
- printf("%d\n",ans);
- return ;
- }
bzoj 1495的更多相关文章
- BZOJ 1495 [NOI2006]网络收费(暴力DP)
题意 给定一棵满二叉树,每个叶节点有一个状态0/10/10/1,对于每两个叶节点i,ji,ji,j,如果这两个叶节点状态相同但他们的LCALCALCA所管辖的子树中的与他们状态相同的叶节点个数较少(少 ...
- BZOJ 2127: happiness [最小割]
2127: happiness Time Limit: 51 Sec Memory Limit: 259 MBSubmit: 1815 Solved: 878[Submit][Status][Di ...
- BZOJ 3144: [Hnoi2013]切糕
3144: [Hnoi2013]切糕 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1495 Solved: 819[Submit][Status] ...
- BZOJ 3275: Number
3275: Number Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 874 Solved: 371[Submit][Status][Discus ...
- BZOJ 2879: [Noi2012]美食节
2879: [Noi2012]美食节 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 1834 Solved: 969[Submit][Status] ...
- bzoj 4610 Ceiling Functi
bzoj 4610 Ceiling Functi Description bzoj上的描述有问题 给出\(n\)个长度为\(k\)的数列,将每个数列构成一个二叉搜索树,问有多少颗形态不同的树. Inp ...
- BZOJ 题目整理
bzoj 500题纪念 总结一发题目吧,挑几道题整理一下,(方便拖板子) 1039:每条线段与前一条线段之间的长度的比例和夹角不会因平移.旋转.放缩而改变,所以将每条轨迹改为比例和夹角的序列,复制一份 ...
- 【sdoi2013】森林 BZOJ 3123
Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负整数 ...
- 【清华集训】楼房重建 BZOJ 2957
Description 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多少栋房子. 为了简化问题,我们考虑这些 ...
随机推荐
- #6278. 数列分块入门 2(询问区间内小于某个值 xx 的元素个数)
题目链接:https://loj.ac/problem/6278 题目大意:中文题目 具体思路:数列分块模板题,对于更新的时候,我们通过一个辅助数组来进行,对于原始的数组,我们只是用来加减,然后这个辅 ...
- mysql 原理 ~ redo
一 简介:redo log二 文件 ib_logfile0 ib_logfile1 两个redo log 默认为一组 循环覆盖写入三 相关参数 innodb_log_file_size=256 ...
- Flask三种导入配置文件的方式
# 配置对象,里面定义需要给 APP 添加的一系列配置 class Config(object): DEBUG = True # 从配置对象中加载配置 app.config.from_object(C ...
- Android自定义控件三种方式
1.组合原生控件(继承自ViewGroup.LinearLayout.FrameLayout.RelativeLayout等) 将原生空间做组合,自定义一些事件 2.自己绘制控件(继承自View) ...
- logistic回归为什么要使用sigmoid函数
https://www.baidu.com/link?url=LnDjrhLG7Fx6YVgR9WljUILkPZrIzOR402wr2goIS-ARtDv9TwZ2VYVbY74fyVpQlE22n ...
- 20165234 《Java程序设计》第二周课下作业
1. 教材代码完成情况测试P14 把100改为自己的后四位学号,编译运行Kernighan.java 代码的功能是从给定一个数字,实现从1依次加到此数的和. 如下是我用命令行实现代码的编译与运行. 2 ...
- 微信支付-H5网页支付开通流程
简介 H5 支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在前述页面确认使用微信支付时,商户发起本服务呼起微信客户端进行支付.主要用于触屏版的手机浏览器请求微信支付的场景.可以方便的从外 ...
- Sql server—— for xml path简单用法(可以按照分组把相同组的列中的不同的值,像字符串一样拼接在一起显示在分组之后的列中。)
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAI8AAACWCAIAAABo2EyXAAAKeklEQVR4nO2dy27rNh7G+U7CFIrfZX
- sqlmap 使用笔记
1.sqlmap -hh 查看详细说明 2.使用google proxychains sqlmap -g " inurl:\".php?id=1\" " 自动发 ...
- ActiveMQ使用
一.Windows安装ActiveMQ 1.下载解压 2.启动服务 二.Linux安装ActiveMQ 1.下载解压 2.启动访问 三.队列模式 1.创建maven项目 2.生产者 3.消费者 四.主 ...