题解 [UR #17] 滑稽树上滑稽果
好题!
首先可以反证得出最优解一定是从根开始的一条链
然后就不会做了
然后暴力就可以阶乘枚举,特殊性质就可以贪心了
然后有一个暴力 DP 不太容易想到
令 \(mask\) 为所有滑稽果共有的位
那么最优策略下一定是先将其它位都弄成 0
令 \(f_s\) 为当前还有哪些位不为 0,达到这一状态所需的最小代价
转移枚举任意一个 \(a_i\) 转移
不需要考虑这个 \(a_i\) 是否已经用过,因为用过一定不优
这样是 \(O(n2^{\log V})=O(nV)\) 的
不容易想到主要是因为 \(a_i\) 的重复使用和「哎呀转移要枚举好多东西,肯定不对啦」之类的想法
怎么优化呢?
发现每个状态能转移到的状态数是有限的(哎呀其实就是枚举子集)
那么考虑预处理一个状态是否能转移到另一个
可以预处理出对于每个 \(i\),是否存在一个滑稽果与之无交
那么转移枚举一个无交可行的子集就好了
这样的复杂度是 \(O(3^{18})\) 或者说 \(O(3^{log_2^{a}})=O(a^{log_2^3})\approx O(a^{1.58)}\)
然后对数不会用了:
- 关于对数常见公式证明:
- \(\log_a^{m^n}=n\log_a^m\):\[\begin{aligned} a^{\log_a^{m^n}}&=m^n \\ &=(a^{log_a^m})^n \\&=a^{n\log_a^m} \end{aligned} \]\[\therefore \log_a^{m^n}=n\log_a^m \]
- \(\log_{a^n}^m=\frac{1}{n}\log_a^m\):\[\begin{aligned} (a^n)^{\log_{a^n}^m}&=m \\ a^{nlog_{a^n}^m}&=a\log_a^m \\n\log_{a^n}^m&=\log_a^m\\ \log_{a^n}^m&=\frac{1}{n}\log_a^m\end{aligned} \]
- \(n^{\log_a^m}=m^{\log_a^n}\):\[\begin{aligned} \huge n^{\log_a^m}&=\huge m^{\log_m^{n^{\log_a^m}}}\\&=\huge m^{\log_a^m\log_m^n} \\&= \huge m^{\frac{\log_m^n}{\log_m^a}}\\&=\huge m^{\log_a^n}\end{aligned} \]
- \(\log_a^{m^n}=n\log_a^m\):
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define N 100010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
int a[N];
ll f[1<<18];
bool able[1<<18];
signed main()
{
n=read();
for (int i=1; i<=n; ++i) a[i]=read();
int lim=1<<18, mask=lim-1;
for (int i=1; i<=n; ++i) able[(~a[i])&(lim-1)]=1, mask&=a[i];
// for (int s=lim-1; s; --s) able[s^(s&-s)]|=able[s];
for (int s=lim-1; s; --s) if (able[s]) for (int t=s; t; t=s&(t-1)) able[t]|=able[s];
memset(f, 0x3f, sizeof(f));
f[lim-1]=0;
for (int s=lim-1; s; --s) if (f[s]!=INF)
for (int t=s; t; t=s&(t-1)) if (able[t])
f[s^t]=min(f[s^t], f[s]+(s^mask^t));
cout<<f[mask]+1ll*mask*n<<endl;
return 0;
}