题意:统计子数组异或和的约数个数为偶数的个数
解题思想:前缀异或和,平方数
反其道而行之,平方数的约数个数为奇数,那么找到所有子数组异或和为平方数的个数,用总的子数组个数一减,就是答案;
用pre[i,j]表示[i,j]子段的异或和,那么pre[1,j]=pre[1,i-1]^pre[i,j];则有pre[1,j]^pre[i,j]=pre[1,i-1],若pre[i,j]为平方数x*x,那么只需要在[1,j-1]子段中找到满足(x*x)^pre[1,j]的异或和的个数,此处用map记录即可;
代码:
const int maxn=3e5+5;
int a[maxn],b[maxn];
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
int ans = 0, sum = 0;
int x = n, k = 0;
while (x)
{
x /= 2;
k++;
}
b[0]++;
// cout<<(1<<k)<<' '<<k<<'\n';
int t = 0;
for (int i = 1; i <= n; i++)
{
ans ^= a[i];
for (int j = 0; j * j < (1 << k); j++)
{
sum += b[ans ^ (j * j)];//统计前i-1子段中满足子数组为平方数的个数
}
b[ans]++;
t = max(t, ans);
}
for (int i = 0; i <= t; i++)
{
b[i] = 0;
}
cout << n * (n + 1) / 2 - sum << '\n';
}
题意:找到满足L*L正方形内数字均大于等于L的方格,最大化其边长L
解题思路:二分check边长L,若a[i][j]>=L,则标记为1,反之为0,二维前缀和对全图进行标记,枚举正方块一角,若该方块和值等于L*L则符合要求,从而最大化L
注意题目对二维前缀和的应用,以及将数字简化为0,1两类的思维
代码:
int n, m;
bool check(int x, vector<vector<int>> &a)
{
vector<vector<int>> q(n + 1, vector<int>(m + 1));
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (a[i][j] >= x)
q[i][j] = 1;
else
q[i][j] = 0;
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
q[i][j] += q[i - 1][j] + q[i][j - 1] - q[i - 1][j - 1];
}
}
for (int i = n; i + 1 - x >= 1; i--)
{
for (int j = m; j + 1 - x >= 1; j--)
{
int t = q[i][j] - q[i - x][j] - q[i][j - x] + q[i - x][j - x];
//找到面积为x*x的方块进行check
if (t == x * x)
return true;
}
}
return false;
}
void solve()
{
cin >> n >> m;
vector<vector<int>> a(n + 1, vector<int>(m + 1));
//题目给定n*m<=1e6,n<=m,最好使用vector存储!!!
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> a[i][j];
}
}
int l = 1, r = m;
while (l < r)
{
int mid = (l + r + 1) / 2;
if (check(mid, a))
{
l = mid;
}
else
r = mid - 1;
}
cout << l << '\n';
}
反思:多思考,多用脑,前缀和异或的学习,注意01简化思考,真的很有用!!!