题目:F. Array Stabilization (AND version)

题目链接:https://codeforces.com/contest/1579/problem/F

题意:给出一个n(1 <= n <= 1e6)个元素的数组a,数组元素只有0和1。对数组a进行操作,每次操作将数组每个元素都向右移d位(超过n个就回到第1个),得到数组b,将数组a的每个元素都和对应下标的数组b中的元素按位取和,得到新的数组a。求将数组a的所有元素都化为0的最小操作数。

输入:第一行输入测试用例个数t。

      t 个测试用例。每个测试用例第一行输入数组元素个数n(1 <= n <= 1e6)和d。第二行输入n个元素。

输出:输出将数组元素都化为0的最小操作数,若不能都化为0,则输出-1。

样例输入:

1

5 2

1 1 0 1 0

样例输出:

3

样例解释:

第一次操作:a = 【1 1 0 1 0】, b = 【1 0 1 1 0】, a = 【1 0 0 1 0】

第二次操作:a = 【1 0 0 1 0】, b = 【1 0 1 0 0】, a = 【1 0 0 0 0】

第三次操作:a = 【1 0 0 0 0】, b = 【0 0 1 0 0】, a = 【0 0 0 0 0】

题目分析:模拟。模拟右移多少次0能将所有下标都覆盖到。

解题步骤:用cnt标记下标已经为0的元素的个数,用set容器存当前元素为0且右移d位的元素为1的下标。在模拟每次右移d位的时候(用cnt 是否等于n判断循环条件),若set容器为空,则直接输出-1。否则遍历set容器,若set容器的元素的下标为0且右移d位的元素为1,则将该元素放进vector v1,用以删除(若不删除,则set容器元素过多会超时),cnt++ ,并判断右移d位后的右移d位是否为1,若是,则将右移d位后的元素的下标放进vector v2,用以遍历元素后插入元素。

可将set容器中的元素删除的原因:

例如a = 【0 1 1 1】,d = 1,首先将0(下标从0开始)放进set,遍历0时,因为a[0]  = 0且a[0 + d] = a[1] = 1,故可将0删除。因为接下来0右移2位就相当于1右移1位,0右移3 位就相当于1右移2位。

AC代码:

#include<bits/stdc++.h>

 

using namespace std;

const int N = 1e6 + 10;

int a[N], n, d;

 

void solve(){

       scanf("%d %d", &n, &d);

      

       set<int> s;

       int cnt = 0;

       for(int i = 0;i < n;i++) scanf("%d", &a[i]);

       for(int i = 0;i < n;i++){

              if(a[i] == 0){

                     cnt++;

                  if(a[(i + d) % n] == 1)    s.insert(i);

              }

       }

      

       if(cnt == n){

              printf("0\n");

              return;

       }

      

       int ans = 0;

       while(cnt < n){

              ans++;

              if(s.empty()){

                     printf("-1\n");

                     return;

              }

             

              vector<int> v1, v2;

              for(set<int>::iterator it = s.begin();it != s.end();it++){

                     int x = (*it + d) % n;

                     if(a[x] == 1){

                            cnt++, a[x] = 0;

                            v1.push_back(*it);

                            if(a[(x + d) % n] == 1)    v2.push_back(x);

                     }

              }

             

              for(int i = 0;i < v1.size();i++) s.erase(v1[i]);

              for(int i = 0;i < v2.size();i++) s.insert(v2[i]);

       }

       printf("%d\n", ans);

}

 

int main(void){

       int t;

       scanf("%d", &t);

       while(t--) solve();

      

       return 0;

}

时间复杂度:O(nlogn),因为每个元素最多进set一次,出set一次。

空间复杂度:O(n)。