http://poj.org/problem?id=3274
题意,给出n,k,k表示接下来的数 用k位二进制表示,
给出n个数,
求出 最长的一个区间,满足: 该区间整体上,二进制下的每一位(1到k)上 “1”的个数之和 都相等。
思路: 首先是先把n个数都转成一个 【n】【k】的01数组,然后怎么找这个区间【i,j】呢?
sum[i][x] 表示 从1到第i头牛时,一共在x位一共有多少个1
因为要求这个区间【i,j】满足 sum[i][0]-sum[j][0] = sum[i][1]=sum[j][1] =......=sum[i][k]=sum[j][k];
即,从i到j,每一位的增量相等,
将上式左右交换一下可得:
sum[i][1]-sum[i][0]=sum[j][1]-sum[j][0]
....................
sum[i][k]-sum[i][0] =sum[j][k]-sum[j][0]
设CC[x][k]=sum[i][k]-sum[i][0]
显然,等号左边就是 第i头牛时, 把它的sum数组的每一位减去 第0位得到的一个数组CC[i]
如果cc[i] 的每一位 等于cc[j],那么就会符合 从第i到第j条牛之间 每一位的增量相等.
那么我们预处理得到cc数组之后,直接把这个cc[i]hash成一个 数, 一会排个序,判断hash值相同的 数 相差最远的便是答案
trick, 这样得到的是 cc[j]-cc[i]一定会是 【每一位增量相等】,那么max长度就是j-i, 但是如果存在 cc[i]这个节点本身就是 每一位 均等时, (也就是i的每一位都是1 或0) 此时长度应为 j-i +1
所以只在放多 一个 每一位全是0的节点即可
<span style="font-size:14px;">#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
typedef unsigned __int64 ull;
ull p=239;
__int64 tm[100005];
__int64 single[100005][35];
__int64 sum[100005][35];
struct node
{
ull ss;
__int64 pos;
node(){}
};
node ss[100005];
__int64 cmp( node a, node b)
{
if (a.ss!=b.ss)
return a.ss<b.ss;
else
return a.pos<b.pos;
}
int main()
{
ull n,i,j,k;
scanf("%I64d%I64d",&n,&k);
ull tmp;
for (i=0;i<k;i++)
single[1][i]=0;
for (i=2;i<=n+1;i++)
{
scanf("%I64d",&tm[i]);
ull times=k,tmp=tm[i];
while(times--)
{
single[i][times]=tmp%2; //求每一位
tmp>>=1;
}
}
for (i=1;i<=n+1;i++)
{
for (j=0;j<k;j++)
{
sum[i][j]=single[i][j]+sum[i-1][j]; //前缀和
}
}
for (i=1;i<=n+1;i++)
{
ull tmp=0;
for (j=0;j<k;j++)
{
sum[i][j]-=sum[i][k-1]; //减去最右边
tmp=tmp*p+sum[i][j]; //拉链法求hash值
}
ss[i].ss=tmp; //hash值
ss[i].pos=i; //位置
}
sort(ss+1,ss+n+1+1,cmp);
ull maxx=0;
for (i=1;i<=n+1;i++)
{
ull tmpi=i;
while (ss[i].ss==ss[i+1].ss&&i<=n ) //选择 相同hash值,距离最远的2个数
i++;
if (ss[i].pos-ss[tmpi].pos>maxx) //更新max
maxx=ss[i].pos-ss[tmpi].pos;
}
printf("%I64d\n",maxx);
return 0;
}
</span>