Luogu P2843 暗杀

原题链接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
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/ZmeetL/p/11801446.html