目录
A. Counting Passes
B. Minimize Abs 1
C. Minimize Abs 2
D. Counting Ls
E. Mex and Update
F. Minimize Bounding Square
思路:直接遍历一遍,记录有多少个人的得分大于等于L。
代码:
void solve()
{
cin >> n >> m;
int sum = 0;
for(int i = 1; i <= n; i ++)
{
int t;
cin >> t;
if(t >= m) sum ++;
}
cout << sum << endl;
}
思路:如果输入的数是比l小的答案为l,如果输入的数在l到r的范围内,则答案为数本身,如果输入的数大于r的答案为r。
代码:
void solve()
{
int l, r;
cin >> n >> l >> r;
for(int i = 1; i <= n; i ++)
{
int t;
cin >> t;
if(t <= l)
{
cout << l << ' ';
}
else if(l < t && t < r)
{
cout << t << ' ';
}
else cout << r << ' ';
}
}
思路:直接暴力枚举x和y就好。枚举到x的平方大于n的时候break。枚举的时候注意y的取值,可以是sqrt(abs(n - x * x)) + 1, 或者是 sqrt(abs(n - x * x))。int sqrt()的时候 :因为返回类型是整数,结果只保留整数部分,小数部分将被舍去。
代码:
void solve()
{
int l, r;
cin >> n;
int sum = n;
for(int i = 1; i * i <= 2e12; i ++)
{
int t = i * i;
if(t > n) break;
int p = abs(n - t);
int q = sqrt(p);
int su = abs(t + q * q - n);
q ++;
sum = min(sum, su);
su = abs(t + q * q - n);
sum = min(sum, su);
}
cout << sum << endl;
}
思路:题目要求
我们设三个点的坐标是(x1, y1), (x2, y2), (x3, y3) 其中 x2 = x1 ,y2 = y3。所以我们只需要找出第二个点 (x2, y2),对答案的贡献就是(row[i] - 1) * (col[j] - 1) 其中row[i] 表示 第i行有多少个元素是o,col[j] 表示第j行有多少个元素是o。
代码:
int n, m, k;
int row[2020], col[2020];
char c[2020][2020];
void solve()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= n; j ++)
{
cin >> c[i][j];
if(c[i][j] == 'o') row[i] ++, col[j] ++;
}
}
int sum = 0;
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= n; j ++)
{
if(c[i][j] == 'o')
{
sum += ((row[i] - 1) * (col[j] - 1));
}
}
}
cout << sum << endl;
}
思路:运用set和map,map存每个数出现的次数,set存每个数是否出现过,当一个数的出现次数变成0后,set将其加入,反之当一个数出现次数大于0的时候,set将其删除。首先,我们先把0到n都存到set里面去(初始时0 - n在数组中都没出现过).。 然后按照题目要求模拟即可,每次操作后的答案就是set中存的第一个元素。set中存的元素是排好序的。
代码:
void solve()
{
cin >> n;
cin >> m;
map mp;
set s;
for(int i = 0; i <= n; i ++)
{
s.insert(i);
}
for(int i = 1; i <= n; i ++)
{
cin >> a[i];
mp[a[i]] ++;
s.erase(a[i]);
}
for(int i = 1; i <= m; i ++)
{
int op, l, r;
cin >> l >> r;
mp[a[l]] --;
if(mp[a[l]] == 0) s.insert(a[l]);
a[l] = r;
mp[a[l]] ++;
s.erase(r);
cout << *s.begin() << endl;
}
}
思路:二分答案,利用前缀和优化。
x和y这两个元素可以分开看,相当于一维里面的两个直线。
如果正方形边长越大,我们需要的移动次数越少,相反,正方形边长越小,需要移动的次数越多。这里有一个单调性,因此我们可以二分最终的正方形边长。边长固定了,考虑到最优情况下,覆盖的线段必定有一端点上的点是从来没移动过的,因此我们可以枚举这个作为边界的点。然后计算需要移动的次数。
我们先将坐标从小到大排序,然后预处理出一个前缀坐标和。注意需要分别枚举作为左端点的点和作为右端点的点。
两个维度分别取所需移动次数的最小值,其和不超过k则边长可行。
(代码中我把k的变量名定义为m了。抱一丝qwq)
代码:
int x[N], y[N], sumx[N], sumy[N];
int fun(int a[], int s[], int mid)
{
int ans = 1e18;
for(int i = 1; i <= n; i ++)
{
int t = i * a[i] - s[i];
int R = a[i] + mid;
int l = 1, r = n;
while(l < r)
{
int midd = l + r >> 1;
if(a[midd] > R) r = midd;
else l = midd + 1;
}
if(a[l] > R)
{
t += s[n] - s[l - 1] - (n - l + 1) * R;
}
ans = min(ans, t);
}
for(int i = n; i >= 1; i --)
{
int t = s[n] - s[i - 1] - (n - i + 1) * a[i];
int L = a[i] - mid;
int l = 1, r = n;
while(l < r)
{
int midd = l + r + 1 >> 1;
if(a[midd] < L) l = midd;
else r = midd - 1;
}
if(a[l] < L)
{
t += l * L - s[l];
}
ans = min(ans, t);
}
return ans;
}
bool check(int mid)
{
int t = fun(x, sumx, mid) + fun(y, sumy, mid);
return t <= m;
}
void solve()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++)
{
cin >> x[i] >> y[i];
}
sort(x + 1, x + 1 + n);
sort(y + 1, y + 1 + n);
for(int i = 1; i <= n; i ++)
{
sumx[i] = x[i] + sumx[i - 1];
sumy[i] = y[i] + sumy[i - 1];
}
int l = 0, r = 1e9;
while(l < r)
{
int mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
cout << l << endl;
}
链接:TOYOTA SYSTEMS Programming Contest 2023(AtCoder Beginner Contest 330) - AtCoder