题意:将一串长度为 n ∗ m n*m n∗m 的 01 01 01 字符串,挨个地将字符放到一个 n ∗ m n * m n∗m 的矩阵中,并求出当第 i i i 个字符放入矩阵后,矩阵有多少个行与列存在字符 1 1 1 ,并依次输出答案。
每次取出字符串的队头元素,放入坐标为 ( 1 , 1 ) (1,1) (1,1) 的位置,而矩阵原来的元素往后移动一格,例如: ( 1 ≤ j ≤ m − 1 ) (1 \leq j \leq m - 1) (1≤j≤m−1) 放入一个新的元素后, j j j 变成 j + 1 j+1 j+1 ,如果 ( i < n , j = m ) (i < n,j = m) (i<n,j=m) 放入一个新的元素后, i i i 变成 i + 1 i+1 i+1, j j j 变成 1 1 1。
思路:求放入第 i i i 元素后,矩阵中有多少个行与列存在字符 1 1 1 。可以将行与列分开计算,最后输出 c o l [ i ] + r o w [ i ] col[i] + row[i] col[i]+row[i] , c o l [ i ] 、 r o w [ i ] col[i]、row[i] col[i]、row[i] 分别表示当放入第 i i i 个值后,有 c o l [ i ] col[i] col[i] 列存在字符 1 1 1 ,有 r o w [ i ] row[i] row[i] 行存在字符 1 1 1。
列计算:将新的元素加入矩阵中后,会发现原来的元素在 1 ∽ m 1\backsim m 1∽m 列中循环移动,当某一时刻 m m m 列都存在 1 1 1 时,此后加入的元素将不再影响列的数量,将 n ∗ m n * m n∗m 个元素分成长度为 m m m 的 n n n 个组,对于每组的为 1 1 1 的元素,若所处的列没有 1 1 1 ,则 c o l [ i ] + 1 col[i] + 1 col[i]+1 ,并标记当前列已存在 1 1 1。(列的计算与 1 1 1 的所在位置无关,只与当前列是否被标记有关)
行计算:行计算与列计算的方式是完全不同的,主要原因是行为逐行推进,而列是一个循环,且与 1 1 1 所在的位置是相关的。在模拟样例的时候,我们能发现 a 1 a_1 a1 在任意一行的情况都不会超过 m m m 种,情况如下所示:
a 1 a_1 a1
a 2 a 1 a_2a_1 a2a1
a 3 a 2 a 1 a_3a_2a_1 a3a2a1
. . . ... ...
a m a m − 1 . . . a 2 a 1 a_ma_{m-1}...a_2a_1 amam−1...a2a1
这便是 a 1 a_1 a1 在一行中的所有情况,对于每种情况,对剩余的元素以 m m m 个元素为一组,进行分组。若分组中存在 1 1 1 ,则表示当前行为 1 1 1 ,再利用前缀和计算出,到当前行时,有多少行存在元素 1 1 1 。在计算的时候可以用双指针 O ( n ∗ m ) O(n * m) O(n∗m)
#include<stdio.h>
const int N = 1e6 + 10;
char s[N];
int res[N],row[N],col[N];
int main() {
int t;
scanf("%d",&t);
while(t --) {
int n,m;
scanf("%d %d %s",&n,&m,s+1);
for(int i=0;i<=n*m;i++) {
res[i] = 0;
row[i] = 0;
col[i] = 0;
}
int L = 1,cnt = 0;
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
if(s[L] == '1' && col[j] == 0) {
col[j] = 1;
cnt ++;
}
res[L ++] = cnt;
}
}
L = 1,cnt = 0;
int l = 1,r = 0;
while(L <= n * m) {
r ++;
if(s[r] == '1') cnt ++;
if(r - l + 1 > m) {
if(s[l] == '1') cnt --;
l ++;
}
row[L % m] += cnt > 0 ? 1 : 0;
res[L] += row[L % m];
L ++;
}
for(int i=1;i<L;i++) printf("%d ",res[i]);
printf("\n");
}
return 0;
}