原题链接https://www.luogu.org/problem/P2843

题目描述

敌方的高级将领都是有很多相同之处的。为了方便找到每名敌将的弱点,我军情报部已经找到了他们的不同点,并将其归纳为K种特性。比如1号特性就代表一个敌将喜欢打人,2号特性就代表一个敌将喜欢吃饭,等等。

为了方便存储,我军使用特性值来描述一个敌将的特点。特性值是一个位数为K的二进制整数,每一位都可以表示一名敌将的一个特性。1代表具有此特性,0代表没有。

我军间谍打听到,不久有N个敌方将领会举行一场宴会,而且入场时他们会排成一路纵队入场。如果有连续的M个人的每种特性出现的次数之和是一样的,那么我军间谍就很容易暗杀这M个人。你需要帮助我军算出,间谍最多可以暗杀多少人?

因为间谍开始攻击后就可能被敌人击毙,所以间谍只能进行一次攻击。

样例说明:
ai         属性           敌将
7          1 1 1            1
6          0 1 1            2
7          1 1 1            3
2          0 1 0            4
1          1 0 0            5
4          0 0 1            6
2          0 1 0            7
可以击杀第3人到第6人,共6-3+1=4人

由题意可知我们要找到一段区间其中各个特征和相等,用一个sum数组记录各个特征的前缀和,我们可以得出如果区间[l, r]中各个特征和相等, 那么
\[sum[l][2] - sum[l][1] = sum[r][2] - sum[r][1]\]

\[sum[l][3] - sum[l][1] = sum[r][3] - sum[r][1]\]

​ ....

证明:

当区间[l, r]中各个特征和相等, 设和的值为x
\[sum[r][1] = sum[l][1] + x\]

\[sum[r][2] = sum[l][2] + x\]

​ ...

所以前缀和的相对差值不变。
将前缀和相对差值进行hash,映射到数组中然后用hash值作为key进行匹配,最后进行差值比较确认是否符合。

#include<iostream>
#include<cstdio>
#include<vector>
#define ll long long
using namespace std;

const int N = 1e5 + 10, M = 40, MAX = 1e6 + 10;

int n, k, ans, num;
int lg[M];
int a[N][M], sum[N][M];

vector<int>  hash[MAX];

template <typename T>
inline void read(T &x){
    x = 0;
    char c = getchar();
    T op = 1;
    for(; c < '0' || c > '9'; c = getchar())
        if(c == '-')    op = -1;
    for(; c <= '9' && c >= '0'; c = getchar())
        x = (x << 3) + (x << 1) + c - '0';
    x *= op;
}

inline bool check(int x, int y){
    for(int i = 1; i <= k; ++i)
        if(a[x][i] != a[y][i])  return 0;
    return 1;
}//进行精准检验

inline void HASH(int x){
    int p = 0;
    for(int i = 1; i <= k; ++i)
        p += a[x][i] * lg[i], p %= MAX;
    p = (p + MAX) % MAX;
    int s = hash[p].size();
    for(int i = 0; i < s; ++i) //在这人之前是否可以找到一个人进行匹配
    if(ans < (x - hash[p][i]))
        if(check(x, hash[p][i]))
            ans = x - hash[p][i];
    hash[p].push_back(x);
    return;
}//加入到key为p的hash数组中

inline void init(){
    read(n), read(k);
    ans = 0, lg[1] = 1;
    for(int i = 2; i <= k; ++i) lg[i] = (lg[i - 1] * 71) % MAX;
    hash[0].push_back(0); //要对第一个人就符合条件进行判断
}

int main(){
    init();
    for(int i = 1; i <= n; ++i){
        read(num);
        for(int j = 1; j <= k; ++j, num >>= 1) //进行数字分解并进行前缀和计算
            sum[i][j] = sum[i - 1][j] + (num & 1), a[i][j] = sum[i][j] - sum[i][1]; // 计算相对差值
        HASH(i);
    }
    printf("%d\n", ans);
    return 0;
}
01-04 06:16