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>