Codeforces Round #841 (Div. 2) and Divide by Zero 2022
C. Even Subarrays(前缀异或)
题目大意
给定一个数组,求满足所有元素异或的结果有偶数个因子的子数组的个数。本题将0视作有奇数个因子。
解题思路
直接求不好求,我们可以求异或结果有奇数个因子的子数组。
对于一个数,我们可以将其分解为\(p_1^{c_1}p_2^{c_2}\cdots p_n^{c_n}\),\(p_i\)均为质数。
其因子的个数为\((c_1 + 1)(c_2 + 2)\cdots (c_n + 1)\),要使其乘积为奇数,则所有的\(c_i\)都应为奇数,则该数为完全平方数。
在题目给定的范围中,完全平方数并不多,可以暴力枚举。
注意n以内的数字异或结果\(\le 2*n\)
可以用前缀异或的方式快速判断是否有子数组的异或结果为特定的数字。
具体见代码
参考代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
vector<int> squ;
int a[N];
int cnt[N * 4];//记录前缀异或出现过的数据,注意数组大小
#define ll long long
void work()
{
int n;
cin >> n;
ll ans = 1ll * n * (n + 1) / 2;
for (int i = 1; i <= n; ++i)
{
cin >> a[i];
a[i] ^= a[i - 1];
for (auto j : squ)
ans -= cnt[a[i] ^ j];
//关键在这里,若a[i] ^ j的结果出现过,我们记a[k] = a[i] ^ j,k < i
//a[k] ^ a[k] = a[j] ^ a[k] ^ j
//即a[j] ^ a[k] = j
//即从下标k到j的元素异或结果为j,那么改子数组不应作为答案的一部分,需要减去
++cnt[a[i]];//记录前缀异或
}
cout << ans << endl;
for (int i = 1; i <= n; ++i)
--cnt[a[i]];
}
int main()
{
for (int i = 0; i * i < N * 2; ++i)
{
squ.push_back(i * i);
}
++cnt[0];
ios::sync_with_stdio(0);
cout.tie(0);cin.tie();
int T;
cin >> T;
while (T--)
{
work();
}
return 0;
}
D. Valiant's New Map(二维前缀和)
题目大意
给定一个二维数组,要求找到满足限制条件的最大正方形,
限制条件为:正方形内所有元素都不小于该正方形的边长。
解题思路
显然可以二分答案,解题的关键如何快速判断正方形是否满足该限制条件。
对于当前正方形边长x,我们可以预处理二维数组,将不小于x的元素置一,反之,置零。
计算出新数组的二位前缀和,若某个正方形所有元素的和等于\(x^2\),则该正方形满足题意。
参考代码
#include <bits/stdc++.h>
using namespace std;
void work()
{
int n, m;
cin >> n >> m;
vector<vector<int>> a(n + 1, vector<int> (m + 1));
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
{
cin >> a[i][j];
}
}
auto check = [&](int x)
{
vector<vector<int>> s(n + 1, vector<int> (m + 1));
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
{
s[i][j] = a[i][j] >= x ? 1 : 0;
}
}
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j<= m; ++j)
{
s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
}
}
for (int i = 1; i <= n - x + 1; ++i)
{
for (int j = 1; j <= m - x + 1; ++j)
{
if (s[i + x - 1][j + x - 1] - s[i - 1][j + x - 1] - s[i + x - 1][j - 1] + s[i - 1][j - 1] == x * x)
{
return true;
}
}
}
return false;
};
int l = 1, r = n, ans = 1;
while (l <= r)
{
int mid = (l + r) / 2;
if (check(mid))
{
ans = mid;
l = mid + 1;
}
else
{
r = mid - 1;
}
}
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;
cin >> T;
while (T--)
{
work();
}
return 0;
}