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

题目

低智选手果然刷不动uoj

首先考虑一下构造一棵树显然是骗你玩的,按位与这个东西越做越小,挂到链的最下面显然不会劣于挂到之前的某一个点下面,所以我们只需要求一个排列使得答案最小就好了

设\(A=\max(a_i)\),发现最优答案不可能要劣于反复对一个数取\(\rm and\)的答案,我们就有了一个\(O(nA)\)的暴力,设\(dp_i\)表示当前的\(\rm and\)和为\(i\),把这个\(i\)变成\(0\)的最小代价

但是有可能最后的\(\rm and\)和也不是\(0\),于是我们把所有\(a_i\)都共有的二进制位\(k\)都求出来,在把每一个\(a_i\)消去这些数位,即和\(k\)异或一下,最后的\(\rm and\)和就一定为\(0\);在答案加上\(n\times k\)就好了

这样的话转移非常简单,我们枚举一个\(a_j\),\(dp_i=\min(dp_{i\ \rm and \ a_j }+i\ \rm and \ a_j)\)即可

最后答案是\(\min(dp_{a_i}+a_i)\)

注意到\(i\ \rm and \ a_j\)一定是\(i\)的子集,考虑枚举\(i\)的子集\(j\),现在只需要判断是否存在一个\(a_k\)满足\(i\ \rm and\ \ a_k=j\)

从集合的角度来考虑,我们可以把上面那个条件拆成\(j\)是\(a_k\)的子集,并且\(a_k\)是\(j\bigoplus i\)在全集补集中的子集,我们用\(\rm fwt\)处理一下就可以知道是否有一个\(a_k\)是\(i\bigoplus j\)在全集补集中的子集,但并没有办法判断\(j\)是否为\(a_k\)的子集

但是想一想发现我们没有必要判断\(j\)是否为\(a_k\)的子集,只管转移就好了

观察转移式\(dp_i=\min(dp_j+j)\),显然\(j\)越小越好,如过存在\(a_k\)是\(i\bigoplus j\)在全集补集中的子集,但是\(a_k\)并不是\(j\)的子集,那么一定会有一个更小的\(i\)的子集是这个\(a_k\)的子集,那个转移一定更优

于是复杂度就是\(O(3^{\log A})\)

#include<bits/stdc++.h>
#define re register
#define LL long long
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
inline int read() {
    char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=1e5+5;
int n,a[maxn],vis[1<<18+1],k,T,len;
LL dp[1<<18+1],ans;
int main() {
    n=read();
    for(re int i=1;i<=n;i++) a[i]=read(),T=max(T,a[i]);
    len=1;while(len<T) len<<=1;len--;
    for(re int i=1;i<=n;i++) vis[a[i]]=1;
    for(re int i=2;i<=len+1;i<<=1)
        for(re int ln=i>>1,l=0;l<len;l+=i)
            for(re int x=l;x<l+ln;++x) vis[x+ln]|=vis[x];
    k=a[1];for(re int i=2;i<=n;i++) k&=a[i];
    for(re int i=1;i<=n;i++) a[i]^=k;
    memset(dp,20,sizeof(dp));
    ans=dp[0];dp[0]=0;
    for(re int i=1;i<=T;i++)
        for(re int j=i;j;j=(j-1)&i)
            if(vis[len^j]&&dp[i^j]+(i^j)<dp[i]) dp[i]=dp[i^j]+(i^j);
    for(re int i=1;i<=n;i++)
        ans=min(ans,dp[a[i]]+a[i]);
    printf("%lld\n",1ll*n*k+ans);
    return 0;
}

原文地址:https://www.cnblogs.com/asuldb/p/11388166.html

时间: 2024-08-05 03:32:40

uoj#370【UR #17】滑稽树上滑稽果的相关文章

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

一个显然的结论是最终树的形态必然是一条链.具体证明只要考虑选定树上的某一条链,然后把其他部分全部接在它后面,这样答案一定不会变劣. 那么,一开始的想法是考虑每一位的最后出现位置,但这并不容易实现.注意到最终序列是单调递减的.我们在统计答案之前,把公共位先统计掉,即始终都是1的位.这样,剩下的位的最终结果都是0.这样,我们就避免了在统计时忽略某些数.那么,我们记dp[i]表示当前的结果为i的最小费用.我们在转移时枚举下一个数字是什么.这里无需担心数字的重复放置,因为它并不能让当前的数字发生变化.那

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\

[UOJ]#33. 【UR #2】树上GCD

题目大意:给定一棵有根树,边长均为1,对于每一个i,求树上有多少个点对,他们到lca距离的gcd是i.(n<=200,000) 做法:先容斥,求出gcd是i的倍数的点对,考虑长链剖分后从小到大合并计算答案,小的部分先把每个深度的数量变为每个深度的倍数的数量,然后若深度>k,直接到大的里面暴力,若深度<=k,我们在大的里面维护f[i][j]表示深度mod i(1<=i<=k)为j的点数,理论上k取n^0.5时达到最小复杂度O(n^1.5),实际上k比较小的时候常数较小.另外递归

UOJ【UR #12】实验室外的攻防战

题意: 给出一个排列$A$,问是否能够经过以下若干次变换变为排列$B$ 变换:若${A_i> A_i+1}$,可以${swap(A_i,A_i+1)}$ 考虑一个数字从A排列到B排列连出来的路径与其他数字是否相交,相交就表示大小关系需要判断,(类似于二维偏序)用线段树维护区间最小值即可. 权值为1,2的线分别与权值为4的线相交,而且4在它们左边,所以需要判断它们的大小关系,发现${4>1}$,${4>2}$,所以满足条件. 1 #include<iostream> 2 #in

【UR #2】树上GCD

这道题是有根树点分治+烧脑的容斥+神奇的分块 因为是规定1为根,还要求LCA,所以我们不能像在无根树上那样随便浪了,必须规定父亲,并作特殊讨论 因为gcd并不好求,所以我们用容斥转化一下,求x为gcd的因数的个数,这样就可以随便统计了,个人觉得代码比题解要好懂. 又因为统计完重心的所有子树,还有重心的父亲,所以在这个分支块内沿着重心的父亲一路向上爬,这时候重心的子树到重心的父亲的距离是变的,所以我们用神奇的分块大法,分类讨论,$<=\sqrt{n}$使用数组记录答案,方便以后再用到的时候统计,$

[UOJ #180][UR #12]实验室外的攻防战(树状数组)

Description 时针指向午夜十二点,约定的日子——2月28日终于到来了.随着一声枪响,伏特跳蚤国王率领着他的跳蚤大军们包围了 picks 博士所在的实验室. 当然,picks 博士不会坐以待毙,他早就率领着他的猴子们在实验室外修筑了许多的坚固防御工事. 经过跳蚤侦察兵的勘察,跳蚤国王发现 picks 博士的防御工事有着 n 处薄弱点,于是他把他的跳蚤大军分成了 n 支小队,并打算让它们分别进攻每一个薄弱点.但是因为战场混乱,这 n 支小队的位置被打乱了,重新整队之后,跳蚤国王发现第 i

uoj33 【UR #2】树上GCD

题目 大致是长剖+\(\rm dsu\ on\ tree\)的思想 先做一个转化,改为对于\(i\in[1,n-1]\)求出有多少个\(f(u,v)\)满足\(i|f(u,v)\),这样我们最后再做一个反演就好了 既然我们要求有多少对\(f(u,v)\)是\(i\)或\(i\)的倍数,我们需要在长剖的时候快速合并两边的信息,这个信息长得非常别致,形如到当前节点距离为\(i\)或\(i\)的倍数的节点个数 轻儿子这边还好说,我们直接暴力调和级数处理一下即可,但是这样的信息从中儿子哪里却非常不好继承

【UOJ #17】【NOIP 2014】飞扬的小鸟

http://uoj.ac/problem/17 dp,注意细节. #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int in() { int k = 0, fh = 1; char c = getchar(); for(; c < '0' || c > '9'; c = getchar()) if (c == '-') fh = -1; for(; c

Test on 09/04/2016

滑稽树  (huajitree.pas/c/cpp) [问题描述] JZYZ的湖畔边有一棵滑稽树,每年的冬天滑稽树上都会长出很多个滑稽果.我们用一个二维平面N,M描述每个滑稽果所能落下的位置,即每个滑稽果不可能落到我们所描述的二维平面之外. 滑稽大师cdc钟爱于收集滑稽果,但是他只有一个篮子,篮子只能放在一个点上,即一个篮子最多收集一个滑稽果.现在滑稽大师cdc想知道他收集到滑稽果的期望值是多少.(cdc放的篮子在任意位置的概率相同) 为了(zao)方(shu)便(ju)起(fang)见(bia