AtCoder Beginner Contest 330 题解

目录

A. Counting Passes

B. Minimize Abs 1

C. Minimize Abs 2

D. Counting Ls

E. Mex and Update

F. Minimize Bounding Square


A. Counting Passes

思路:直接遍历一遍,记录有多少个人的得分大于等于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;
}

B. Minimize Abs 1

思路:如果输入的数是比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 << ' ';
	}
}

C. Minimize Abs 2

思路:直接暴力枚举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;
}

D. Counting Ls

思路:题目要求

  • 正好有两个单元格在同一行。
  • 正好有两个单元格位于同一列
  • 所有三个单元格中都写有一个 o。

 我们设三个点的坐标是(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;
}

E. Mex and Update

思路:运用set和map,map存每个数出现的次数,set存每个数是否出现过,当一个数的出现次数变成0后,set将其加入,反之当一个数出现次数大于0的时候,set将其删除。首先,我们先把0到n都存到set里面去(初始时0 - n在数组中都没出现过).。 然后按照题目要求模拟即可,每次操作后的答案就是set中存的第一个元素。set中存的元素是排好序的。

代码:

void solve()
{
	cin >> n;
	cin >> m;
	map<int, int> mp;
	set<int> 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;
	}
	
}

F. Minimize Bounding Square

思路:二分答案,利用前缀和优化。

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值