【GDKOI2017模拟1.12】与运算

Description

给出一个序列,Fi为前i项进行and运算之后的值。求这个序列的一个排列,使得 Fi 最大。
输出这个最大值。
n<=10^6

Solution

首先考虑F数组,显然是单调不升的。
那么我们考虑Dp,Dp i表示F数组目前最后一位为i的最大和。注意可以不放满。
那么我们枚举一个数转移,复杂度是O(N^2)的。
转移的话我们可以预处理cnt[i]表示有多少个数and i等于i的。
这个东西可以用经典分治求出来,把二进制每一位分成0和1,把1的答案加到0上。
然后转移就显然了。
然后我们发现如果我们把转移看做很多次减去一个2的幂,那么每一次我们只需要把i的二进制减去一个1就好了。
每次转移可以拆成很多次转移嘛~

Code

#include <cstdio>
#include <cstring>
#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 N=1.5*1e6+5,D=20;
int cnt[N],mi[D+5],n,x;
ll f[N],ans;
void solve(int l,int r) {
    if (l==r) return;
    int mid=(l+r)/2;
    solve(l,mid);solve(mid+1,r);
    fo(i,mid+1,r) cnt[i-mid+l-1]+=cnt[i];
}
int main() {
    freopen("and.in","r",stdin);
    freopen("and.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n) scanf("%d",&x),cnt[x]++;
    mi[0]=1;
    fo(i,1,D) mi[i]=mi[i-1]*2;
    solve(0,mi[D]-1);
    f[mi[D]-1]=cnt[mi[D]-1]*(mi[D]-1);
    fd(i,mi[D]-1,0) {
        fo(j,0,D) if (i&mi[j]) {
            x=i-mi[j];
            f[x]=max(f[x],f[i]+(ll)x*(cnt[x]-cnt[i]));
        }
        ans=max(ans,f[i]);
    }
    printf("%lld\n",ans);
}

你可能感兴趣的:(位运算,dp,与运算,GDKOI2017)