POJ 3274 Gold Balance Lineup

抽象一下,此题给你10W个数,让你求一个[i,j],使得a[i]到a[j]的数的二进制每一位上的1出现的次数相等,并使j-i+1最大。
解法:
      我们把每个数化为二进制把每一位对应地保存在一个长为k的数组l,l[i][j]的值就表示第i个数的第j位是1还是0。
      令p[i][j]表示把前i项的第j位累加起来的值,即p[i][j]=l[i][j]+l[i-1][j]+...+l[1][j];
      最关键的一步来了,把这n个数p[i][j]都减去p[i][1],那么只需要寻找一对x,y,使得p[x][i]==p[y][i], 1<=i<=k,记录最大的y-x为答案。
为什么?
      因为如果[x,y]上二进制每一位上的1出现的次数相等,那么
      对于任意的a,b,有p[x][a]-p[y][a] =p[x][b]-p[y][b] (1<=a<=k,1<=b<=k)
                      => p[x][a]-p[x][b]=p[y][a]-p[y][b]
      我令b=1,p[x][i]把每一位上的都减去p[x][1]后,必然p[x][i]=p[y][i] (1<=i<=k)
      所以我只需找到一个最大的y-x即可。
那么此时,问题便转化为了,有个二维数组p,找到一对x,y,使得p[x][i]==p[y][i], 1<=i<=k,求max(y-x)。
显然O(n^2)会超时,于是就用到了hash。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#define MAXHASH 100001
using namespace std;
int n,len,l[100020][35],ans,a[100020],fk[100020][35];
vector<int> v[100020];
int jdz(int n)
{
    return n>0?n:-n;
}
int f(int c)
{
    int ret =0;
    for (int i =1; i <=len; i++)
        ret+=fk[c][i]*i;
    return jdz(ret);

}
bool judge(int a,int b)
{
    int i;
    for (i=1; i<=len; i++)
    {
        if (fk[a][i] != fk[b][i])
            break;
    }
    if (i == len+1)
        return true;
    else
        return false;
}
int main()
{
    int i,j,t;
    scanf("%d%d",&n,&len);
    for (i=1; i<=n; i++)
    {
        scanf("%d",a+i);
       // printf("\n");
    }
    memset(l,0,sizeof(l));
    for(i=1;i<=n;i++)
    {
        t=a[i];
        for(j=1;j<=len;j++)
        {
            l[i][j]=l[i-1][j]+t%2;
            t=t/2;
        }
    }
    for(i=1;i<=n;i++)
        for(j=1;j<=len;j++)
            fk[i][j]=l[i][j]-l[i][1];
    for (i=0; i<100020; i++)
        v[i].clear();
    ans=0;
    for (i=0; i<=n; i++)
    {
        t=f(i);
        t%=MAXHASH;
        for (j=0; j<v[t].size(); j++)
        {
            if (judge(v[t][j],i))
            {
                ans=ans<jdz(i-v[t][j])?jdz(i-v[t][j]):ans;

                break;
            }
        }
        if (j == v[t].size())
            v[t].push_back(i);
    }
    printf("%d\n",ans);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值