POJ3274-牛的属性-HASH-ACM

原题POJ3274

参考:进击的阿俊

 


 

已知有n头牛,用一个K位二进制数Ak,Ak-1,...,A1表示一头牛具有的特征,Ai=1表示具有特征i。现给定按顺序排列的N头牛的k位特征值,称某个连续范围内“特征平衡”,假如在这个范围内,拥有各个特征的牛的数量都相等。求最大“特征平衡”连续范围。

分析:

用sum[i][j]( 1<=i<=n, 1<=k<=j)表示1到第i头牛中具有特征j的牛的数量。问题转化为求解满足sum[i][l] - sum[j][l] = sum[i][1] - sum[j][1](l = 1,2,..,k)的最大i - j的值。很容易想到最简单的方法,通过令d = n to 1,判断是否存在i,使得sum[i + d][j] - sum[i][j] = sum[i + d][1] - sum[i][j],时间复杂度为O(n*n*k)。由于n的最大值能达到100000,必须选择一个更加优化的方法。

1)容易验证,sum[i][l] - sum[j][l] = sum[i][1] - sum[j][1] ( l = 1,2,..,k ) 等价于sum[i][l] - sum[i][1] = sum[j][l] - sum[j][1] ( l = 1,2,...k )。因此令d[i][j] =  sum[i][j] - sum[i][1] ,问题就转化为求解使得d[i][j] = d[i + size][j]的最大size。

2)为进一步简化算法,对于任意 1<= i <=n, 令sig[i] = (d[i][1] + d[i][2] + ... +d[i][k] ) % m (m为一个较大的质数)。这样,若对于i和j, sig[i] != sig[j],那么必定不会满足d[i][] = d[j][],就无需再对它进行验证;若满足sig[i] = sig[j],才需要进一步确定是否有d[i][] = d[j][]。

3)用h[k] (1 <= k <= m,m为以上取模运算的素数)记录满足sig[i] = k的i值。通过令 i = 1 to n,以此更新h[sig[i]]和largest,即可得到结果。

 


 

样例输入

 

7 3
7
6
7
2
1
4
2

 

样例输出


4


图示

 

 


 

//
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>

using namespace std;

const int maxn = 100010;
const int maxk = 31;
int n, k, tmp;
bool cow[maxn][maxk];
int sum[maxn][maxk];
int d[maxn][maxk];	//d 和 sig辅助计算哈希值
int s, size;
const int prime = 49117;
int sig[maxn];
int largest;

vector <int> h[prime];		//哈希表

void search(int i, int t)//0 1 \ 2 2 \ 2 3 \ 3 4 \ 
{
    int size = h[i].size();
    for (int j = 0; j < size; j++) {
        bool flag = 1;
        for (int l = 0; l < k; l++) {
//printf("d[h[%d][%d]:%d][%d]:%d ==? d[%d][%d]:%d\n",i,j,h[i][j],l,d[h[i][j]][l],t,l,d[t][l]);
            if ( d[ h[i][j] ][l] != d[t][l] ) {
                flag = 0;
                break;
            }
        }
        if (flag) {
            if (t - h[i][j] > largest)
                largest = t - h[i][j];
            return;
        }
    }
    h[i].push_back(t);
//printf("i:%d push back t:%d\n",i,t);
}


int findLargest()
{
    largest = 0;
    for (int i = 1; i <= n; i++) {
        search(sig[i], i);
		printf("%d\n",largest);
    }
    return largest;
}

void init()
{
    memset(sum, 0, sizeof(sum));
    memset(sig, 0, sizeof(sig));
    for (int i = 0; i < prime; i++) h[i].clear();
    h[0].push_back(0);
    for (i = 1; i <= n; i++) {
        for (int j = 0; j < k; j++) {
            sum[i][j] = sum[i - 1][j] + cow[i][j];
            d[i][j] = sum[i][j] - sum[i][0];
			sig[i] += d[i][j];
//printf("%d ",d[i][j]);
//printf("%d ",d[i][j]);
        }
//printf("%d\n",sig[i]);
		/*
        for (j = 0; j < k; j++) {
            sig[i] += d[i][j];
        }
		*/
        sig[i] = abs(sig[i]) % prime;
    }
}

int main()
{
    //while (1) {
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++ ) {
        scanf("%d", &tmp);
        for (int j = 0; j < k; j++) {
            cow[i][j] = tmp % 2;
            tmp /= 2;
        }
    }
    init();
    findLargest();
    printf("%d\n", largest);
    //}
    return 0;
}

 

转载于:https://www.cnblogs.com/lvyahui/p/4009939.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值