题解 [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} \]

点击查看代码
#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;
}
posted @ 2022-04-27 09:35  Administrator-09  阅读(4)  评论(0编辑  收藏  举报