BZOJ-4245: [ONTAK2015]OR-XOR

[文章目录]

Description

给定一个长度为n的序列a[1],a[2],...,a[n],请将它划分为m段连续的区间,设第i段的费用c[i]为该段内所有数字的异或和,则总费用为c[1] or c[2] or ... or c[m]。请求出总费用的最小值。1<=m<=n<=500000,0<=a[i]<=10^18)

自己YY,丰衣足食。
拆位,贪心的想,如果能满足更高位为0,那么一定得满足。
如果当前位上有奇数个1,那么不存在变为0的方法。如果有偶数个1,将m段区间n-1个位置看成插m-1个隔板,那么第一个1~第二个1之间的位置就不能插入隔板,第三个1到第4个1之间不能插入隔板,以此推类。如果空的位置足够m-1个,那么可以,反之不可以则继续处理更低位。
细节好像挺多的,或者说我失了智。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
int n,m;
ll a[501000];
bool v[501000];
inline char nc()
{
    static char buf[100000],*p1,*p2;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2) ? EOF : *p1++;
}
inline void read(ll &x)
{
    char ch=nc();
    while(ch<'0'||ch>'9') ch=nc();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=nc();
}
int main()
{
    scanf("%d%d",&n,&m); int cnt,num=n-1; m--;
    for(int i=1;i<=n;++i) read(a[i]);
    for(int i=62;~i;--i)
    {
        cnt=0;
        for(int j=1;j<=n;++j) cnt+=(a[j]&(1ll<<i))?1:0;
        if(cnt&1) continue;
        bool fl=0; int c=num;/*bug3:并不将num还原*/
        for(int j=1;j<n;++j)
        {
            if(a[j]&(1ll/*bug4:不转换成ll*/<<i)) fl^=1;
            if(fl&&!v[j]) num--;
        }
        if(num<m) {num=c; continue;/*bug2:break*/}
        fl=0;
        for(int j=1;j<n;++j)
        {
            if(a[j]&(1ll<<i/*bug1:i写成j*/)) fl^=1;
            v[j]|=fl;
        }
    }
    ll ans=0,now=0;
    for(int i=1;i<=n;++i)
    {
        now^=a[i];
        if(m&&!v[i]) ans|=now,now=0,m--;
    }
    ans|=now;
    printf("%lld",ans);
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注