CF 1439 Codeforces Round #684 (Div. 1)
A1
题意
一个0 / 1二维矩阵,每次操作可以选一个四宫格,并将其中三个数取反,找到在3nm步骤内将矩阵变成全0矩阵的方法。
思路
如果执行上图中的三个操作,四宫格内只有一个位置被操作了奇数次,其他位置都被操作了偶数次,因而相当于没有改变。因此,只需对每一个为1的位置都进行类似的操作就可以了。
A2
题意
一个0 / 1二维矩阵,每次操作可以选一个四宫格,并将其中三个数取反,找到在nm步骤内将矩阵变成全0矩阵的方法。
思路
- 考虑一个四宫格内,如何将步数减少到4步以内。在一个四宫格内共有四种不同的操作。我们发现,按照上面的思路分别将四宫格内的数全部变为0时,有些操作是重复出现的。重复偶数次的步骤相当于不做,因此可以统计每一种操作出现的次数,然后只做出现奇数次的操作。这样,处理一个四宫格内的数最多4次就可以了。
- 考虑只有n* 2(即只有两列)的情况。对于前n-2行,如果这一行有一个1,那么就选择这一个1和下一行的两列做一次操作;如果这一行有两个1,那么就选择这两个1和下一行的一个位置做一次操作;如果没有1,那么就无需做操作了。这样就可以将n * 2的棋盘最终缩小成四宫格。
- 对于nm的棋盘,先处理其中的(n-2)(m-2)的位置,如果mp[i][j]是1,就将mp[i][j],mp[i+1][j],mp[i][j+1]同时操作。这样就可以将棋盘缩成两行+两列,然后,将最后的两行/列分别按照2.中的方法消除,最终变成一个四宫格。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int N = 110;
struct NODE{
int x1, y1, x2, y2, x3, y3;
NODE(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0, int f = 0)
{
x1 = a;
y1 = b;
x2 = c;
y2 = d;
x3 = e;
y3 = f;
}
};
int mp[N][N];
char s[N][N];
int cnt[5];
vector<NODE>ans;
int main()
{
int T;
scanf("%d", &T);
while(T)
{
T--;
ans.clear();
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
scanf("%s", s[i] + 1);
for(int i = 1 ; i <= n; i++)
for(int j = 1; j <= m; j++)
mp[i][j] = s[i][j] - '0';
for(int i = 1; i <= n - 2; i ++)
{
for(int j = 1; j <= m - 2; j++)
{
if(mp[i][j])
{
ans.push_back(NODE(i, j, i + 1, j, i, j + 1));
mp[i][j] ^= 1;
mp[i][j + 1] ^= 1;
mp[i + 1][j] ^= 1;
}
}
}
for(int i = 1; i <= n - 2; i++)
{
if(mp[i][m - 1] && mp[i][m])
{
ans.push_back(NODE(i, m - 1, i, m, i + 1, m - 1));
mp[i][m - 1] ^= 1;
mp[i][m] ^= 1;
mp[i + 1][m - 1] ^= 1;
}
else
{
if(mp[i][m - 1])
{
ans.push_back(NODE(i, m - 1, i + 1, m - 1, i + 1, m));
mp[i][m - 1] ^= 1;
mp[i + 1][m - 1] ^= 1;
mp[i + 1][m] ^= 1;
}
else
{
if(mp[i][m])
{
ans.push_back(NODE(i, m, i + 1, m - 1, i + 1, m));
mp[i][m] ^= 1;
mp[i + 1][m - 1] ^= 1;
mp[i + 1][m] ^= 1;
}
}
}
}
for(int j = 1; j <= m - 2; j++)
{
if(mp[n - 1][j] && mp[n][j])
{
ans.push_back(NODE(n - 1, j, n, j, n - 1, j + 1));
mp[n - 1][j] ^= 1;
mp[n][j] ^= 1;
mp[n - 1][j + 1] ^= 1;
}
else
{
if(mp[n - 1][j])
{
ans.push_back(NODE(n - 1, j, n - 1, j + 1, n, j + 1));
mp[n - 1][j] ^= 1;
mp[n - 1][j + 1] ^= 1;
mp[n][j + 1] ^= 1;
}
else
{
if(mp[n][j])
{
ans.push_back(NODE(n, j, n - 1, j + 1, n, j + 1));
mp[n][j] ^= 1;
mp[n - 1][j + 1] ^= 1;
mp[n][j + 1] ^= 1;
}
}
}
}
cnt[1] = cnt[2] = cnt[3] = cnt[4] = 0;
if(mp[n - 1][m - 1])
{
cnt[1]++;
cnt[2]++;
cnt[3]++;
}
if(mp[n - 1][m])
{
cnt[1]++;
cnt[2]++;
cnt[4]++;
}
if(mp[n][m - 1])
{
cnt[1]++;
cnt[3]++;
cnt[4]++;
}
if(mp[n][m])
{
cnt[2]++;
cnt[3]++;
cnt[4]++;
}
if(cnt[1] % 2)
{
ans.push_back(NODE(n - 1, m - 1, n, m - 1, n - 1, m));
mp[n - 1][m - 1] ^= 1;
mp[n][m - 1] ^= 1;
mp[n - 1][m] ^= 1;
}
if(cnt[2] % 2)
{
ans.push_back(NODE(n - 1, m - 1, n - 1, m, n, m));
mp[n - 1][m - 1] ^= 1;
mp[n - 1][m] ^= 1;
mp[n][m] ^= 1;
}
if(cnt[3] % 2)
{
ans.push_back(NODE(n - 1, m - 1, n, m - 1, n, m));
mp[n - 1][m - 1] ^= 1;
mp[n][m - 1] ^= 1;
mp[n][m] ^= 1;
}
if(cnt[4] % 2)
{
ans.push_back(NODE(n - 1, m, n, m - 1, n, m));
mp[n - 1][m] ^= 1;
mp[n][m - 1] ^= 1;
mp[n][m] ^= 1;
}
printf("%d\n", ans.size());
for(int i = 0; i < ans.size(); i++)
printf("%d %d %d %d %d %d\n", ans[i].x1, ans[i].y1, ans[i].x2, ans[i].y2, ans[i].x3, ans[i].y3);
}
return 0;
}
C - Greedy Shopping
题意
n家商店排成一列,价格 a i a_{i} ai是一个单调不增的序列。有两种操作:
- 将1~x家商店的价格改成 m a x ( a i , y ) max(a_{i}, y) max(ai,y)
- 总y元,从第x家商店开始购物,走到第i家商店时,如果剩下的钱足够 a i a_{i} ai,则消费 a i a_{i} ai元,否则走到下一家,求出一个可以在多少家商店购物。
思路
线段树。
对于1操作,先找出第一个比y小的数的位置pos,如果pos <= x,则修改[pos, x]区间为y。为防止找不到比y小的数,可以在末尾加一个0。
对于2操作,记录区间和,如果区间和小于当前剩余的钱数,答案加上区间长度;否则,继续二分。但是这样处理,遍历到的区间数最大可达到
n
2
\frac{n}{2}
2n,TLE。考虑最坏的情况,即每隔一家商店购物一次的情况。此时,设走到[i, i + 1]区间,
y
≤
a
i
+
a
i
+
1
≤
2
a
i
y \leq a_{i} + a_{i + 1} \leq 2a_{i}
y≤ai+ai+1≤2ai,则有
y
−
a
i
≤
y
2
y-a_{i} \leq \frac{y}{2}
y−ai≤2y,也就是说,每次走到一个可以购物的区间,y至少减少一半。那么总共的可购物区间不超过
l
o
g
2
y
log_{2}{y}
log2y个,如果只对可购物区间进行询问,那么时间复杂度会大大降低。因此,在二分区间之前,先判断这个区间的最小值是否小于钱数,如果小于,则继续二分区间,否则不能在该区间购物。总时间复杂度为O(
q
∗
l
o
g
2
n
∗
l
o
g
2
y
q * log_{2}n * log_{2}y
q∗log2n∗log2y)。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
int val[N * 4], mina[N * 4];
LL sum[N * 4];
bool flag[N * 4];
int a[N];
void pushdown(int x, int l, int r)
{
int mid = (l + r) >> 1;
if(flag[x])
{
val[x * 2] = val[x * 2 + 1] = val[x];
mina[x * 2] = mina[x * 2 + 1] = mina[x];
sum[x * 2] = (LL)val[x] * (LL)(mid - l + 1);
sum[x * 2 + 1] = (LL)val[x] * (LL)(r - mid);
flag[x * 2] = flag[x * 2 + 1] = true;
flag[x] = false;
}
}
void pushup(int x)
{
sum[x] = sum[x * 2] + sum[x * 2 + 1];
mina[x] = mina[x * 2 + 1];
}
void build(int x, int l, int r)
{
if(l == r)
{
val[x] = a[l];
sum[x] = a[l];
mina[x] = a[l];
return;
}
int mid = (l + r) >> 1;
build(x * 2, l, mid);
build(x * 2 + 1, mid + 1, r);
pushup(x);
}
int get_lower(int x, int l, int r, int v)
{
if(l == r)
return l;
int mid = (l + r) >> 1;
pushdown(x, l, r);
if(mina[x * 2] >= v)
return get_lower(x * 2 + 1, mid + 1, r, v);
return get_lower(x * 2, l, mid, v);
}
void update(int x, int l, int r, int ll, int rr, int v)
{
if(r < ll || rr < l)
return;
if(ll <= l && r <= rr)
{
flag[x] = true;
val[x] = v;
mina[x] = v;
sum[x] = (LL)v * (LL)(r - l + 1);
return;
}
pushdown(x, l, r);
int mid = (l + r) >> 1;
update(x * 2, l, mid, ll, rr, v);
update(x * 2 + 1, mid + 1, r, ll, rr, v);
pushup(x);
}
int query(int x, int l, int r, int ll, int rr, int *v)
{
if(r < ll || rr < l || mina[x] > (*v))
return 0;
if(ll <= l && r <= rr && sum[x] <= (LL)(*v))
{
(*v) -= sum[x];
return r - l + 1;
}
if(l == r) return 0;
pushdown(x, l, r);
int mid = (l + r) >> 1;
int ans = query(x * 2, l, mid, ll, rr, v);
ans += query(x * 2 + 1, mid + 1, r, ll, rr, v);
return ans;
}
int main()
{
int n, q;
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
build(1, 1, n + 1);
for(int i = 1; i <= q; i++)
{
int opt, x, y;
scanf("%d%d%d", &opt, &x, &y);
if(opt == 1)
{
int pos = get_lower(1, 1, n + 1, y);
if(pos <= x)
update(1, 1, n + 1, pos, x, y);
}
else
printf("%d\n", query(1, 1, n + 1, x, n, &y));
}
return 0;
}