F. Array Stabilization (AND version)(模拟)
作者:互联网
题目: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)。
标签:右移,cnt,set,Stabilization,int,元素,++,version,Array 来源: https://www.cnblogs.com/www-cnblogs-com-lixuanta/p/15361977.html