题目大意
n, a[i]<=10^6
解法
我们考虑把 n 个数排下来,那它的 f 值一定是成段的,即先有 x 个相同的,然后 y 个相同的,然后 z 个相同的……
然后我们就可以设状态了。设 g[i] 表示当前 f 值为 i 的时候的最大答案。那么转移就是枚举一个 j,然后 g[i]=max(g[j]+i*(cnt[i]-cnt[j])),其中 cnt[x] 表示有多少个 a[i] 包含了 x 这个状态。
所以现在其实就是要求 cnt,可以用分治来求。
代码
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long LL;
const int maxn=1e6+5, MX=25, maxN=2e6+5;
int n,N,a[maxn],er[MX];
LL f[maxN]; //这里的f就是上述的cnt
void dfs_f(int l,int r)
{
if (l==r) return;
int mid=(l+r)>>1;
dfs_f(l,mid), dfs_f(mid+1,r);
int enc=mid+1-l;
fo(i,l,mid) f[i]+=f[i+enc];
}
LL g[maxN];
int main()
{
scanf("%d",&n);
N=(1<<20)-1;
fo(i,1,20) er[i]=1<<(i-1);
fo(i,1,n) scanf("%d",&a[i]);
fo(i,1,n) f[a[i]]++;
dfs_f(0,N);
fd(i,N-1,0)
fo(j0,1,20) if ((i&er[j0])==0)
{
int j=i|er[j0];
g[i]=max(g[i],g[j]+i*(f[i]-f[j]));
}
printf("%lld\n",g[0]);
}