Codeforces Round #789 (Div. 1) B. Tokitsukaze and Meeting

翻译:

德木风正在安排会面。会议厅里有𝑛排和𝑚列的座位。

有𝑛⋅𝑚的学生参加了会议,其中有几个调皮的学生,也有几个认真的学生。学生被从1到𝑛⋅𝑚打分。学生们将依次进入会场。当𝑖-th学生进入会场时,他将坐在第1排的第1列,已经就座的学生将向后移动一个座位。具体来说,位于𝑖-th行𝑗-th(1≤𝑗≤𝑚−1)列的学生将移动到𝑖-th行的(𝑗+1)列,位于𝑖-th行的𝑚-th列的学生将移动到(𝑖+1)行第1-st列。

例如,有一个2排2列的会议厅,如下图所示:


将有4名学生依次进入会场,用二进制字符串“1100”表示,其中“0”代表淘气的学生,“1”代表严肃的学生。会议大厅的座位变化如下:


表示一行或一列,当且仅当该行或一列中至少有一个认真的学生时为好。请在𝑖-th学生进入会场后预测好行和列的数量,为所有𝑖。

输入
第一个包含一个正整数𝑡(1≤𝑡≤10000)——测试用例的数量。

对于每个测试用例,第一行包含两个整数𝑛,𝑚(1≤𝑛,𝑚≤106;1≤𝑛⋅𝑚≤106),表示会场座位有𝑛排、𝑚列。

第二行是长度为𝑛⋅𝑚的二进制字符串𝑠,仅由0和1组成。如果𝑠𝑖等于“0”表示𝑖-th学生是一个顽皮的学生,𝑠𝑖等于“1”表示𝑖-th学生是一个严肃的学生。

保证所有测试用例𝑛⋅𝑚的总和不超过106。

输出
对于每个测试用例,打印带有𝑛⋅𝑚整数的一行—在𝑖-th学生进入会议厅之后的好行和列的数量。

例子
inputCopy
3.
2 - 2
1100
4个2
11001101
2 4
11001101
outputCopy
2 3 4 3
2 3 4 3 5 4 6 5
2 3 3 3 4 4 4 5
请注意
第一个测试用例显示在语句中。

第1排学生进入会议厅后,有2个好的排和列:第1排和第1列。

第二个学生进入会议厅后,有3个好的行和列:第1行,第1列和第2列。

第三个同学进入会场后,4排4列都好了。

第4个学生进入会场后,有3行3列:第2行,第1列和第2列。

思路:

        这个是每次记录带有1的行和列,每次进一个人,都会将之前的人向后压缩。这道题刚开始看起来根本毫无头绪,不过数据范围这么大,不可能每次去单独求,所以应该是奇妙的数学规律思维题。我们考虑每次进来m个人,前边的人都要去之前那一列,所以我们可以根据这个性质来着手去解决。

        行的关系比较好找,一行中,只要有一个1,那么这一行就可以算作有1的,然后往后推,比如m=2,我们第三个人进去的时候是第一个人是去了新的一行,前m个人可以单独处理,因为每次去新的一行的只有那几个人,当有两行是,我们就可以加上之前的那一行,三个人,新的一行就是第一个人,所以我们加上h[1],之后第二行有两个人,我们就加上h[2],然后再不断处理本行的,我们就可以推出行的结论。

        接下来是列,当有了不止一行,列你会发现,它是一直对应着的,比如m=3的时候,我们有4个人,那么我们第四个人的列就对应了第一个人,直接进来第五个人,第六个人,因为都是向后推移,所以一直对应,因此我们只需要简单的加个标记,然后加和就可以得到答案。

代码:

#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <math.h>
#include <stdio.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<tuple>
#include<numeric>
#include<stack>
using namespace::std;
typedef long long  ll;
int n,t;
inline __int128 read(){
    __int128 x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if(ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
inline void print(__int128 x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)
        print(x / 10);
    putchar(x % 10 + '0');
}

int m;
string s;
bool l[1000005];
int h[1000005];
int sum[1000005];
void solv(){
    cin>>n>>m>>s;
    s='@'+s;
    for (int i =1; i<=n*m; i++) {
        sum[i]=sum[i-1]+(s[i]=='1');
    }
    for (int i =1; i<=n*m; i++) {
        if (i>=m) {
            h[i]+=h[i-m];
        }
        h[i]+=(sum[i]-sum[max(i-m, 0)])>0;
    }
    int ff=1;
    int an=0;
    for (int i =1; i<=n*m; i++) {
        if (l[ff]==0&&s[i]=='1') {
            an++;
            l[ff]=1;
        }
        ff++;
        printf("%d ",an+h[i]);
        if (ff==m+1) {
            ff=1;
        }
    }printf("\n");
    for (int i =0; i<=n*m; i++) {
        sum[i]=0;h[i]=0;
        l[i]=0;
    }
    
    
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(); cout.tie();
    cin>>t;
    while (t--) {
        solv();
    }
    return 0;
}
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值