BZOJ_1495_[NOI2006]网络收费_树形DP
BZOJ_1495_[NOI2006]网络收费_树形DP
Description
Input
Output
你的程序只需要向输出文件输出一个整数,表示NS中学支付给网络公司的最小总费用。(单位:元)
Sample Input
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的更多相关文章
- BZOJ1495 [NOI2006]网络收费 【树形dp + 状压dp】
题目链接 BZOJ1495 题解 观察表格,实际上就是分\(A\)多和\(B\)两种情况,分别对应每个点选\(A\)权值或者\(B\)权值,所以成对的权值可以分到每个点上 所以每个非叶节点实际对应一个 ...
- 【bzoj1495】[NOI2006]网络收费 暴力+树形背包dp
题目描述 给出一个有 $2^n$ 个叶子节点的完全二叉树.每个叶子节点可以选择黑白两种颜色. 对于每个非叶子节点左子树中的叶子节点 $i$ 和右子树中的叶子节点 $j$ :如果 $i$ 和 $j$ 的 ...
- BZOJ 1495 [NOI2006]网络收费(暴力DP)
题意 给定一棵满二叉树,每个叶节点有一个状态0/10/10/1,对于每两个叶节点i,ji,ji,j,如果这两个叶节点状态相同但他们的LCALCALCA所管辖的子树中的与他们状态相同的叶节点个数较少(少 ...
- 【BZOJ1495】[NOI2006]网络收费 暴力+DP
[BZOJ1495][NOI2006]网络收费 Description 网络已经成为当今世界不可或缺的一部分.每天都有数以亿计的人使用网络进行学习.科研.娱乐等活动.然而,不可忽视的一点就是网络本身有 ...
- 洛谷 P4297 [NOI2006]网络收费
P4297 [NOI2006]网络收费 题目背景 noi2006 day1t1 题目描述 网络已经成为当今世界不可或缺的一部分.每天都有数以亿计的人使用网络进行学习.科研.娱乐等活动.然而,不可忽视的 ...
- 并不对劲的[noi2006]网络收费
题目略长,就从大视野上复制了. 听上去好像费用流,然而…… ***************************表示略长的题目的分界线************************ 1495: [ ...
- 【简】题解 P4297 [NOI2006]网络收费
传送门:P4297 [NOI2006]网络收费 题目大意: 给定一棵满二叉树,每个叶节点有一个状态(0,1),任选两个叶节点,如果这两个叶节点状态相同但他们的LCA所管辖的子树中的与他们状态相同的叶节 ...
- BZOJ_1864_[Zjoi2006]三色二叉树_树形DP
BZOJ_1864_[Zjoi2006]三色二叉树_树形DP 题意: 分析:递归建树,然后DP,从子节点转移. 注意到红色和蓝色没有区别,因为我们可以将红蓝互换而方案是相同的.这样的话我们只需要知道当 ...
- BZOJ_3573_[Hnoi2014]米特运输_树形DP+hash
BZOJ_3573_[Hnoi2014]米特运输_树形DP+hash 题意: 给你一棵树每个点有一个权值,要求修改最少的权值,使得每个节点的权值等于其儿子的权值和且儿子的权值都相等. 分析: 首先我们 ...
随机推荐
- Spring HTTP Service
基于Spring MVC, 使用Http Service Invoke远程调用方法 (参考: http://blog.csdn.net/hanqunfeng/article/details/43031 ...
- PC和手机怎么实现绝对居中?
示例1(懒人之家): http://www.51xuediannao.com/js/nav/360buy_nav.html 示例2(google官方):
- 【Python】输出程序运行的百分比
对于一些大型的Python程序.我们须要在命令行输出其百分比,显得更加友好,以免被人误会程序陷入死循环.假死的窗口. 关键是利用到不换行的输出符\r,\r的输出.将直接覆盖掉此行的内容. 比方例如以下 ...
- USACO 1.2 Milking Cows (枚举)
标记数组(哈希) 1e6的范围,开一个char数组全然能够,有人为1,无人为0,注意边界就可以.最后线性扫描就可以. 时间复杂度,应该是O(n),n为最后结束的时间. 缺点就是--比較慢 /* ID: ...
- java性能监控工具jcmd-windows
jcmd Sends diagnostic command requests to a running Java Virtual Machine (JVM). Synopsis jcmd [-l|-h ...
- CTP报单状态 OrderStatus全部状态
- javascript 判断字符串是否包换字符串
用"ghiahgiahgia".indexOf("hg"); 返回值>=0为包含,否则就是-1(不包含)
- Java序列化算法
Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的过程.java序列化API提供一种处理对象序列化的标准机 ...
- adb命令具体解释(二)——手机缺失sqlite3时操作数据库的多种解决方式
在android应用开发无处不在SQLite数据库的身影.那么在开发中怎么使用adb命令操作数据库的功能呢? 以下我们将完整的介绍与数据库操作相关的命令集及当手机缺少sqlite3的时候的多种解决方式 ...
- 排序&匿名函数
nums=[11,34234,23,344,123,1,23,124,523,4,12342341,423,43545] nums.sort() print(nums) #这个就是排序,从小到到 匿名 ...