题意
传送门 POJ 3274
题解
前缀和维护 [ 0 , i ] [0,i] [0,i] 区间 k k k 种特征的数量和。满足条件的区间 [ i , j ] [i,j] [i,j],对于任一对不同的特征 k 1 , k 2 k1,k2 k1,k2,都有
c n t [ i ] [ k 1 ] − c n t [ j ] [ k 1 ] = c n t [ i ] [ k 2 ] − c n t [ j ] [ k 2 ] cnt[i][k1]-cnt[j][k1]=cnt[i][k2]-cnt[j][k2] cnt[i][k1]−cnt[j][k1]=cnt[i][k2]−cnt[j][k2]
移项得到
c n t [ i ] [ k 1 ] − c n t [ i ] [ k 2 ] = c n t [ j ] [ k 1 ] − c n t [ j ] [ k 2 ] cnt[i][k1]-cnt[i][k2]=cnt[j][k1]-cnt[j][k2] cnt[i][k1]−cnt[i][k2]=cnt[j][k1]−cnt[j][k2]
即前缀和对应的差值相等。
可以对前缀和差值排序 O ( n k l o g n ) O(nklogn) O(nklogn) 后,遍历 O ( n k ) O(nk) O(nk) 求最大长度。时间复杂度更低的做法是用 H a s h Hash Hash 维护前缀和差值,那么 O ( k ) O(k) O(k) 即可求出以 i i i 为右边界的满足条件的最大长度。
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define mod 49999
#define base 107
#define maxn 100005
#define maxk 35
int n, k, cnt[maxk], dif[maxn][maxk];
vector<int> id[mod];
int Hash(int *d)
{
int res = 0;
for (int i = 0; i < k; i++)
{
res = (res * base + d[i]) % mod;
}
return res < 0 ? res + mod : res;
}
int main()
{
scanf("%d%d", &n, &k);
int res = 0;
id[0].push_back(0);
for (int i = 1; i <= n; i++)
{
int m;
scanf("%d", &m);
for (int j = 0; j < k; j++)
{
cnt[j] += ((m >> j) & 1) ? 1 : 0;
dif[i][j] = cnt[j] - cnt[0];
}
int key = Hash(dif[i]);
bool f = 1;
for (int j = 0; j < id[key].size(); j++)
{
int idx = id[key][j], p = k - 1;
while (p >= 0 && dif[idx][p] == dif[i][p]) --p;
if (p == -1)
{
f = 0;
res = max(res, i - idx);
}
}
if (f) id[key].push_back(i);
}
printf("%d\n", res);
return 0;
}