【做题】uoj#370滑稽树上滑稽果——巧妙dp

一个显然的结论是最终树的形态必然是一条链。具体证明只要考虑选定树上的某一条链,然后把其他部分全部接在它后面,这样答案一定不会变劣。

那么,一开始的想法是考虑每一位的最后出现位置,但这并不容易实现。注意到最终序列是单调递减的。我们在统计答案之前,把公共位先统计掉,即始终都是1的位。这样,剩下的位的最终结果都是0。这样,我们就避免了在统计时忽略某些数。那么,我们记dp[i]表示当前的结果为i的最小费用。我们在转移时枚举下一个数字是什么。这里无需担心数字的重复放置,因为它并不能让当前的数字发生变化。那么,我们就有如下第5档部分分。

memset(dp,0x3f,sizeof dp);
for (int i = 1 ; i <= n ; ++ i)
  dp[arr[i]] = arr[i];
for (int i = MAX - 1 ; i >= 1 ; -- i)
  for (int j = 1 ; j <= n ; ++ j)
    if ((i & arr[j]) < i)
      dp[i&arr[j]] = min(dp[i&arr[j]],dp[i] + (i&arr[j]));

注意到这里需要(i&arr[j])<i,即\(C_uarr[j] \bigcap i \neq \emptyset\)。那么,我们把所有\(C_uarr[j]\)的子集存下来,然后转移时枚举子集就做到dp时复杂度与n无关了。
时间复杂度大概是:\(O(a^{log_23})\)。
看起来很爆炸,但是非常不满。

#include <bits/stdc++.h>
#define rint register int
using namespace std;
typedef long long ll;
const int N = 200010, MAX = 1 << 18;
int arr[N],n,avail[MAX + 5],com;
ll dp[MAX + 5];
#define rev(x) ((x) ^ (MAX - 1))
int main() {
  com = MAX - 1;
  scanf("%d",&n);
  for (rint i = 1 ; i <= n ; ++ i)
    scanf("%d",&arr[i]), com &= arr[i];
  for (rint i = 1 ; i <= n ; ++ i)
    arr[i] ^= com;
  for (rint i = 1 ; i <= n ; ++ i) {
    if (avail[rev(arr[i])]) continue;
    for (rint j = rev(arr[i]) ; j ; j = (j-1) & rev(arr[i]))
      avail[j] = 1;
  }
  memset(dp,0x3f,sizeof dp);
  for (rint i = 1 ; i <= n ; ++ i) dp[arr[i]] = arr[i];
  for (rint i = MAX-1 ; i >= 1 ; -- i) if (dp[i] < dp[0]) {
    for (rint j = i ; j ; j = (j-1) & i) if (avail[j])
      dp[i^j] = min(dp[i^j],dp[i] + (0ll^i^j));
  }
  printf("%lld\n",1ll * com * n + dp[0]);
  return 0;
}

小结:抓住题目的特殊性质是优化的关键。

原文地址:https://www.cnblogs.com/cly-none/p/8628280.html

时间: 2024-10-20 19:15:05

【做题】uoj#370滑稽树上滑稽果——巧妙dp的相关文章

uoj#370【UR #17】滑稽树上滑稽果

题目 低智选手果然刷不动uoj 首先考虑一下构造一棵树显然是骗你玩的,按位与这个东西越做越小,挂到链的最下面显然不会劣于挂到之前的某一个点下面,所以我们只需要求一个排列使得答案最小就好了 设\(A=\max(a_i)\),发现最优答案不可能要劣于反复对一个数取\(\rm and\)的答案,我们就有了一个\(O(nA)\)的暴力,设\(dp_i\)表示当前的\(\rm and\)和为\(i\),把这个\(i\)变成\(0\)的最小代价 但是有可能最后的\(\rm and\)和也不是\(0\),于是

dp uoj370 滑稽树上滑稽果

http://uoj.ac/problem/370 首先糊一个结论 答案组成的肯定是一条链 并且我们一定要用最小的代价把他先变成\(0\) 定义一个状态\(f[S]\)表示当前的滑稽值为\(S\)的最小费用 \[f[S] -> f[S \& a[i]] + S \& a[i]\] 此时复杂度为\(O(na)\) 观察发现转移的顺序与\(a_i\)是无关的 因为他是\(\&\) 起来的 费用就是原先数位上的\(1\) 于是我们就有一个想法 定义状态\(ok[S]\)表示\(S\

【搭楼】做题记录

以后做了题还是在这里写一下,觉得好的再去发题解(感觉无脑发题解意义不大) 也不一定是做了的题,看了没打但觉得不错的也可以发上来 (5.23-5.24 第三次月考被X得相当爽) 5.23 星期六 [贪心]Bzoj4027 HEOI2014 兔子与樱花 要是父亲合并儿子又合并就混乱了.然后发现,反正贡献都是一?能合并就在儿子处合并?贪心. [分块]Bzoj3343 教主的魔法 做之前知道了tag,于是很快就想到了算法.还没打过分块呢,于是先去膜拜了一下别人的代码.自己打出来后各种WA,太晚了没调出来

退役前的做题记录5.0

退役前的做题记录5.0 出于某种原因新开了一篇. [CodeChef]Querying on a Grid 对序列建立分治结构,每次处理\((l,mid,r)\)时,以\(mid\)为源点建立最短路树,这样跨越\(mid\)的点对之间的最短路一定会经过\(mid\),因此两点之间的最短路径就可以描述成最短路树上的两段到根路径.对每棵最短路树处理\(dfs\)序,用树状数组维护权值修改即可. [Wannafly挑战赛4F]线路规划 类似SCOI2016萌萌哒一题,并查集\(f_{i,j}\)表示从

线段树例题及做题误区

学会了一系列的线段树之后发现 除了扫描线还不是很熟之外一些操作基本上是得心应手了. 但是仍是很菜,在此再次深有感悟 以后做题再看题解 直接剁手 我就不信不看题解自己的思路出现错误 每次都当我 有了正确的思路之时 却被一些 很迷的思路 误导去看题解,看完题解之后才恍然大悟 .这点需要注意!!!我想我都窥出正解了为什么不能再多想想呢? 真的是超级没有成就感 感觉是非常难受的 好题被自己一时看了题解毁了这是我作为一个正在学习的人所极不想看见的. . 这道题还不错 对线段树是一个考察 如果能仔细思考的话

后缀自动机做题记录

目录 后缀自动机做题记录 sp1811 sp1812 sp10570 luogu 2463 CF873F TJOI2015 弦论 AHOI2013 差异 HEOI2016/TJOI2016 字符串 HAOI2016 找相同字符 SDOI2016 生成魔咒 ZJOI2015 诸神眷顾的幻想乡 留坑待填 广义SAM 其他 NOI原题练习 后缀自动机做题记录 来填之前的坑了...考后大概会做做有字符串的综合题吧 sp1811 lcs板子,对于第一个串建出SAM,第二个串在上面跑,即可求出对于每一个位置

最大流 总结&amp;&amp;做题记录

最近一直很忙,为了节省时间,从今以后的题解会 一个专题 写一篇. 刷了一些题后,有了以下总结: 模型要点: 1.构造流量平衡,在满足流量平衡的情况下,找到要让什么最大. 2.一般用于判断性问题,即所有从源点流出的边满流(或者所有流入汇点的边满流).所以往往和二分答案结合起来使用. 3.如果答案假设为i+1的时候的图仅仅比假设答案为i的时候的图多了一些点或者边,那么可以在假设答案为i的图 跑过最大流后,加上相应的点和边,继续跑最大流.这样顺序枚举答案,避免了每次重新做最大流和重新构图,许多时候效率

python做题

Python题目 1.打印一个九九乘法表 #!/usr/bin/env python # -*- coding: utf-8 -*- """ __author__ = 'YeXiaodong' __QQ__= '12519460' __Email__ = '[email protected]' """ x = 1 list_table = [] print('九九乘法表'.center(100,' ')) while x < 10: lis

做题神器风靡:在线教育虚火旺盛的罪魁祸首是谁?

在线教育的巨大潜力有目共睹,不管是创业者还是巨头都纷纷杀入其中.在线教育本身囊括的范围极其广阔,从胎教.学龄前教育.中小学教育.高等教育,再到职业教育,乃至细分化的英语教育.技能教育等,构成一个完整的教育生态圈.但让人无奈的是,国内在线教育最火爆的却还是中小学教育. 而且由于国内教育体系本身存在的弊端,导致中小学教育主要以书山题海为工具,向高分发起追逐.在这样的大背景下,在线教育最火爆的是各种做题神器App.做题神器的风靡,让整个在线教育行业看起来形势一片大好.然而,做题神器真的就是解决在线教育