AT_abc388题解

第一次赛时调出前五题,祭!!!!!

曲折的抗争历史:
image
总结: \(CDE\) 都是考察细节的思维题,且仔细观察题目可以得到提示.

\(A\)

直接输出第一个字母和 UPC 就行.

#include <iostream>
#include <cstring>

using namespace std;

char c[114514];

int main()
{
	cin >> (c + 1);
	cout << c[1] << "UPC" << '\n';
 	return 0;
}

\(B\)

模拟,每轮求答案时直接枚举每一轮增长的长度和每一轮所有的蛇,求当前这一轮蛇的 \((l + \text{增长的长度}) \cdot t\) 的最大值就行.

#include <iostream>
#include <cstring>
#define int long long

using namespace std;
const int maxn = 105;

int t[maxn], l[maxn];

signed main()
{
	int n, d;
	cin >> n >> d;
	for (int i = 1; i <= n; ++i)
	{
		cin >> t[i] >> l[i];
	}
	for (int i = 1; i <= d; ++i)
	{
		int maxx = -1;
		for (int j = 1; j <= n; ++j)
		{
			maxx = max(maxx, (l[j] + i) * t[j]);
		}
		cout << maxx << '\n';
	}
	return 0;
}

\(C\)

注意到:

  • \(A_i \leq A_{i+1} \ (1 \leq i < N)\) .

可以看到,所有的元素是单调不降的,这便启发我们用二分算法.
对于所有 \(i \in [1, n-1],i\in \text{Z}\)\(a_i\) ,我们往后二分查找第一个大于等于 \(2\cdot a_i\) 的元素所在的位置.
于是,当前的元素能够向后匹配的个数便是 \(n - 搜索到的位置 + 1\) .

#include <iostream>
#include <cstring>
#include <algorithm>
#define int long long

using namespace std;
const int maxn = 5e5 + 10;

int a[maxn];

signed main()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; ++i)
	{
		cin >> a[i];
	}
	int ans = 0;
	for (int i = 1; i <= n - 1; ++i)
	{
		int xb = lower_bound(a + 1, a + n + 1, 2 * a[i]) - a;
		ans += (n - xb + 1);
	}
	cout << ans << '\n';
	return 0;
}

可是这道题,有没有线性的做法呢?
显然是有的.
我们可以再搞一个指针 \(p\) .显然 \(a\) 是单调不降的,因此我们每一次遍历 \(a\) 数组的时候,可以不断地向后调整 \(p\) 的位置.

#include <iostream>

using namespace std;
typedef long long ll;
const int maxn = 5e5 + 10;

int a[maxn];

int main()
{
  int n;
  cin >> n;
  for (int i = 1; i <= n; ++i)
  {
    cin >> a[i];
  }
  int p = 2;
  ll ans = 0;
  for (int i = 1; i <= n; ++i)
  {
    while (p <= n && 2 * a[i] > a[p])
      p++;
    ans += n - p + 1;
  }
  cout << ans << '\n';
  return 0;
}

\(D\)

群里在探讨这道题是差分,其实我觉得算法名字没必要记,有些所谓的算法是可以自己推的.
注意到:

每一个拥有至少一块石头的成年人都会把一块石头作为祝贺礼物送给刚刚成年的外星人.

从这个地方可以看出两个约束:
1.只有成年人才能送给别人石头.
2.一个只有 \(0\) 块石头的人无法再送出石头了(虽然感觉这是废话).

又注意到:

\(i\) 个外星人目前有 \(A_i\) 块石头,将在恰好 \(i\) 年后成年.

因为 \(i\) 是单调上升的,所以我们遍历每个外星人的时候,当前外星人前面的所有外星人一定都比当前外星人成年的时间要早.
考虑遍历每一个外星人,显然,如果不考虑第二个约束,第 \(i\) 个外星人是可以获得 \(i - 1\) 个石头的.
那在遍历每一个外星人的时候,我们是可以记录当前外星人送石头的-有效期-(从哪开始就送不了了),即开一个数组 \(t\) , \(t_i\) 记录有多少个有效期是从 \(i\) 这个位置开始的,再在每一次遍历的时候搞一个继承,就能求出第 \(i\) 个位置会被少送多少个石头,最后统计答案的时候减去就好了.
对于每一个输出,我们可以考虑第 \(i\) 个位置向后面所有的位置送出石头还剩多少个石头(别忘了和 \(0\)\(\text{max}\) ).

#include <iostream>
#define int long long

using namespace std;
const int maxn = 5e5 + 10;

int a[maxn];
int t[maxn];

signed main()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; ++i)
	{
		cin >> a[i];
	}
	for (int i = 1; i <= n; ++i)
	{
		t[i] += t[i - 1];
		a[i] += i - 1;
		a[i] -= t[i];
		if (a[i] < n - i)
		{
			t[i + a[i] + 1]++;
		}
		cout << max(0ll, a[i] - 1ll * (n - i)) << ' ';
	}
	return 0;
}

这道题还有在复杂度上更劣的做法:小根堆.
维护一个小根堆,使得里面存的都是石头的数量不为零的数据.

#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#define pii pair <int, int>

using namespace std;
const int maxn = 5e5 + 10;

int delta; // 表示小根堆里的每一个数都要加上这个数.
int a[maxn]; 
int ans[maxn];
priority_queue <pii, vector <pii>, greater <pii> > q; //第一个数维护石头个数,第二个数维护下标.

int main()
{
  int n;
  cin >> n;
  for (int i = 1; i <= n; ++i)
  {
    cin >> a[i];
    a[i] += q.size(); // 这是显然的.
    delta--; // 因为送礼物,小根堆里的每一个石头个数都要减一.
    q.push({a[i] - delta, i}); // 这里因为后面判断是否为零的时候要 +delta ,所以这里先 -delta .
    while (!q.empty() && q.top().first + delta == 0) // 将小根堆内为石头个数零的数据弹出.
    {
      ans[q.top().second] = 0;
      q.pop();
    }
  }
  while (!q.empty())
  {
    ans[q.top().second] = q.top().first + delta;
    q.pop();
  }
  for (int i = 1; i <= n; ++i)
  {
    cout << ans[i] << ' ';
  }
  return 0;
}

\(E\)

这道题显然和 \(C\) 差不多.
同样的,

  • \(A_i \leq A_{i+1} \ (1 \leq i < N)\)

于是,我们可以使用二分算法.
其次,考虑贪心:既然我们想要求出最大的匹配,那么显然 \(\left[\dfrac{n}{2}\right]\) 是期望的最大的匹配个数.
所以,我们取前面 \(\left[\dfrac{n}{2}\right]\) 的区间,往后找第一个它可以匹配的位置,并将后面的区间的左端点加一,表示左端点之前的位置已经被用过了.
关于这个贪心为什么是对的:如果前面的区间取的是 \(\left[\dfrac{n}{2}\right] - 1\) ,那么如果前面区间的第一个位置可以匹配整个数组的第 \(\left[\dfrac{n}{2}\right]\) 个位置,为什么不让第一个位置去匹配第 \(\left[\dfrac{n}{2}\right] + 1\) 位置,并让第 \(\left[\dfrac{n}{2}\right]\) 个位置尽可能地尝试向后匹配?

#include <iostream>
#include <bits/stdc++.h>
#define int long long

using namespace std;
const int maxn = 5e5 + 10;

int fr[maxn];
int lf = 1, rf;
int ba[maxn];
int lb = 1, rb;
int ans = -1;
void erfen(int l, int r, int val)
{
	if (l > r)
		return ;
	int mid = l + (r - l) / 2;
	if (ba[mid] >= val)
	{
		ans = mid;
		erfen(l, mid - 1, val);
	}
	else
	{
		erfen(mid + 1, r, val);
	}
}

signed main()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; ++i)
	{
		int x;
		cin >> x;
		if (i <= (n / 2))
			fr[++rf] = x;
		else
			ba[++rb] = x;
	}
	int cnt = 0;
	for (int i = lf; i <= rf; ++i)
	{
		ans = 0;
		erfen(lb, rb, 2 * fr[i]);
		int now = ans;
		if (now > rb || now < lb)
		{
			cout << cnt << '\n';
			return 0;
		}
		++cnt;
		lb = now + 1;
	}
	cout << cnt << '\n';
	return 0;
}
posted @ 2025-01-11 23:16  SigmaToT  阅读(51)  评论(0)    收藏  举报