问题重述:
已知有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,即可得到结果。
AC代码
1 //Memory: 28828K Time: 469MS 2 #include <iostream> 3 #include <cstring> 4 #include <cstdio> 5 #include <vector> 6 #include <cmath> 7 8 using namespace std; 9 10 const int maxn = 100010; 11 const int maxk = 31; 12 int n, k, tmp; 13 bool cow[maxn][maxk]; 14 int sum[maxn][maxk]; 15 int d[maxn][maxk]; 16 int s, size; 17 const int prime = 49117; 18 int sig[maxn]; 19 int largest; 20 21 vector <int> h[prime]; 22 23 void search(int i, int t) 24 { 25 int size = h[i].size(); 26 for (int j = 0; j < size; j++) { 27 bool flag = 1; 28 for (int l = 0; l < k; l++) { 29 if ( d[ h[i][j] ][l] != d[t][l] ) { 30 flag = 0; 31 break; 32 } 33 } 34 if (flag) { 35 if (t - h[i][j] > largest) 36 largest = t - h[i][j]; 37 return; 38 } 39 } 40 h[i].push_back(t); 41 } 42 43 44 int findLargest() 45 { 46 largest = 0; 47 for (int i = 1; i <= n; i++) { 48 search(sig[i], i); 49 } 50 return largest; 51 } 52 53 void init() 54 { 55 memset(sum, 0, sizeof(sum)); 56 memset(sig, 0, sizeof(sig)); 57 for (int i = 0; i < prime; i++) h[i].clear(); 58 h[0].push_back(0); 59 for (int i = 1; i <= n; i++) { 60 for (int j = 0; j < k; j++) { 61 sum[i][j] = sum[i - 1][j] + cow[i][j]; 62 d[i][j] = sum[i][j] - sum[i][0]; 63 } 64 for (int j = 0; j < k; j++) { 65 sig[i] += d[i][j]; 66 } 67 sig[i] = abs(sig[i]) % prime; 68 } 69 } 70 71 int main() 72 { 73 //while (1) { 74 scanf("%d%d", &n, &k); 75 for (int i = 1; i <= n; i++ ) { 76 scanf("%d", &tmp); 77 for (int j = 0; j < k; j++) { 78 cow[i][j] = tmp % 2; 79 tmp /= 2; 80 } 81 } 82 init(); 83 findLargest(); 84 printf("%d\n", largest); 85 //} 86 return 0; 87 }