博客广场/ 陈俊霖
比赛总结

8月Day8(未完成)

A.Longest k-Good Segment 核心思路 采用滑动窗口(双指针)维护一个合法区间,保证区间内不同元素个数不超过 kk。枚举右端点,若加入新元素后不同元素个数超过 kk,则移动左指针缩小区间,直到合法。在每次合法时更新最长区间的左右端点。 具体步骤 读入 n,kn,k 和数组 aa(下标从 0 开始)。 初始化左指针 left = 0,不同元

A.Longest k-Good Segment

核心思路

采用滑动窗口(双指针)维护一个合法区间,保证区间内不同元素个数不超过 k。枚举右端点,若加入新元素后不同元素个数超过 k,则移动左指针缩小区间,直到合法。在每次合法时更新最长区间的左右端点。

具体步骤

  1. 读入 n,k 和数组 a(下标从 0 开始)。

  2. 初始化左指针 left = 0,不同元素计数 distinct = 0,计数数组 cnt(记录窗口内各数值出现次数)。

  3. 初始化最优长度 len = 0,最优左右端点 ansL = 0, ansR = -1

  4. 枚举右端点 right 从 0 到 n−1:

    • a[right] 加入窗口:若 cnt[a[right]] == 0,则 distinct++;然后 cnt[a[right]]++

    • distinct > k,循环移动左指针:

      • a[left] 移出窗口:cnt[a[left]]--;若 cnt[a[left]] == 0,则 distinct--left++
    • 当前合法区间长度为 right - left + 1,若大于 len,则更新 lenansL = leftansR = right

  5. 输出 ansL + 1ansR + 1(题目下标从 1 开始)。

题解

#include <bits/stdc++.h>
using namespace std;

const int N = 1000000;

int main() 
{
	int n, k;
	scanf("%d%d", &n, &k);
	vector<int> a(n);
	for (int i = 0; i < n; i ++) scanf("%d", &a[i]);
	
	vector<int> cnt(N + 1, 0);
	
	int left = 0, d = 0;
	int len = 0, l = 0, r = -1;
	
	for (int right = 0; right < n; right ++) 
	{
		int x = a[right];
		if (cnt[x] == 0) d ++;
		cnt[x] ++;
		
		while (d > k) 
		{
			int y = a[left];
			cnt[y] --;
			if (cnt[y] == 0) d--;
			left ++;
		}
		
		int curLen = right - left + 1;
		if (curLen > len) 
		{
			len = curLen;
			l = left;
			r = right;
		}
	}
	
	printf("%d %d", l + 1, r + 1);
	return 0;
}

B.Range Update Point Query

核心思路

只有数值 ≥10 的位置才会在“数位和”操作中发生变化,且每次操作后数值严格减小,最终变为一位数。用有序集合 set 维护所有当前值 ≥10 的位置。操作 1 时,只需在区间内处理这些位置,计算数位和并更新,若结果 <10 则从集合中删除;否则保留,等待后续再次变化。每个位置至多被处理有限次(约 3~4 次),总时间复杂度 O((n+q) log n)。

具体步骤

  1. 读入测试组数 t

  2. 对每组数据:

    • 读入 n, q 和数组 a[1..n]

    • 初始化空集合 st,将所有 a[i] ≥ 10 的下标 i 插入。

    • 循环处理 q 个操作:

      • 若操作为 1 l r

        • st.lower_bound(l) 找到区间内第一个可能变化的位置。

        • 当迭代器指向的位置 ≤ r 时:

          • 计算该位置的数位和 val,更新 a[pos] = val
          • val < 10,则从集合中删除该位置(it = st.erase(it));否则 ++it
      • 若操作为 2 x:直接输出 a[x]

  3. 每组数据结束后换行(如有多个输出)。

题解

#include <bits/stdc++.h>
using namespace std;

int workOut(int x) 
{
	int sum = 0;
	while (x) 
	{
		sum += x % 10;
		x /= 10;
	}
	return sum;
}

int main() 
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t;
	cin >> t;
	while (t--) 
	{
		int n, q;
		cin >> n >> q;
		vector<int> a(n + 1);
		set<int> st;
		
		for (int i = 1; i <= n; i ++) 
		{
			cin >> a[i];
			if (a[i] >= 10) st.insert(i);
		}
		
		while (q--)
		{
			int op;
			cin >> op;
			if (op == 1) 
			{
				int l, r;
				cin >> l >> r;
				auto it = st.lower_bound(l);
				while (it != st.end() && *it <= r) 
				{
					int pos = *it;
					int val = workOut(a[pos]);
					a[pos] = val;
					if (val < 10) it = st.erase(it);
					else it ++;
				}
			} 
			else 
			{
				int x;
				cin >> x;
				cout << a[x] << '\n';
			}
		}
	}
	return 0;
}

C.Tracking Segments

核心思路

利用单调性:若前 mid 次修改后存在美丽区间,则修改次数更多时该区间仍然美丽,因此可二分最早修改次数。检查时,用前缀和快速统计前 mid 个被修改位置上 1 的个数,然后遍历所有给定区间,判断是否存在满足 2 * ones > len 的区间。

具体步骤

  1. 读入测试组数 t

  2. 对每组数据:

    • 读入 n, m,存储 m 个区间 (l, r)

    • 读入 qq 个修改位置(存入数组 c,下标从 0 开始)。

    • 先调用 check(q) 检查所有修改完成后是否已有美丽区间;若无,输出 -1 并继续下一组。

    • 否则二分答案:左边界 l = 1,右边界 r = q,维护 ans = q

      • 计算 mid = (l + r) / 2,若 check(mid) 为真,则更新 ans = midr = mid - 1;否则 l = mid + 1
    • 输出 ans

  3. check(mid) 实现:

    • 创建长度为 n+1 的前缀和数组 pre,初始化为 0。
    • 将前 mid 个修改位置 c[0..mid-1]pre 中标记为 1。
    • 计算前缀和 pre[i] += pre[i-1]
    • 遍历所有区间,若存在 2 * (pre[r] - pre[l-1]) > (r - l + 1),返回 true,否则返回 false

题解

#include <bits/stdc++.h>
using namespace std;

int n, m, q;
vector<pair<int, int>> a;
vector<int> c;

bool check(int mid) 
{
	vector<int> pre(n + 1, 0);
	for (int i = 0; i < mid; i ++) 
	{
		pre[c[i]] = 1;
	}
	for (int i = 1; i <= n; i ++) 
	{
		pre[i] += pre[i - 1];
	}
	for (auto &seg : a) 
	{
		int l = seg.first, r = seg.second;
		int len = r - l + 1;
		int ones = pre[r] - pre[l - 1];
		if (2 * ones > len) return true;
	}
	return false;
}

int main() 
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t;
	cin >> t;
	while (t --) 
	{
		cin >> n >> m;
		a = vector<pair<int,int>>(m);
		for (int i = 0; i < m; i ++) 
		{
			cin >> a[i].first >> a[i].second;
		}
		cin >> q;
		c = vector<int>(q);
		for (int i = 0; i < q; i ++) cin >> c[i];
		
		if (!check(q)) 
		{
			cout << -1 << '\n';
			continue;
		}
		
		int l = 1, r = q, ans = q;
		while (l <= r) 
		{
			int mid = (l + r) / 2;
			if (check(mid)) 
			{
				ans = mid;
				r = mid - 1;
			} 
			else 
			{
				l = mid + 1;
			}
		}
		cout << ans << '\n';
	}
	return 0;
}

D.Fountains

核心思路

分三类情况计算最大美丽值:①用金币买一座、钻石买一座;②用金币买两座;③用钻石买两座。分别求最大值,取三者最大。
对于单一货币买两座,先将所有价格 ≤ 预算的喷泉按价格排序,用前缀最大值记录价格不超过当前位置的最大美丽值,再枚举较贵的喷泉,二分查找价格不超过剩余预算的最便宜喷泉位置,更新答案。

具体步骤

  1. 读入 n, c, d,按货币类型将喷泉分别存入 coin(金币)和 dia(钻石)列表。

  2. 计算混合购买:

    • 遍历 coin,找出价格 ≤ c 的最大美丽值 bestC;遍历 dia,找出价格 ≤ d 的最大美丽值 bestD
    • 若两者都存在,则用 bestC + bestD 更新答案。
  3. 计算同币种购买:

    • coindia 分别调用 getTwo(items, budget),得到该货币下买两座喷泉的最大美丽值(若不足两座则返回 -1),更新答案。
  4. getTwo 实现:

    • 过滤出价格 ≤ budget 的喷泉,存入 v

    • v.size() < 2 返回 -1。

    • v 按价格升序排序。

    • 构建前缀最大值数组 prepre[i] 表示前 i 个喷泉(按价格排序)中的最大美丽值。

    • 枚举 i 从 1 到 m-1(将 v[i] 作为较贵的那座):

      • 计算剩余预算 remain = budget - v[i].second
      • v[0..i-1] 中二分查找价格 ≤ remain 的最大下标 pos
      • 若存在,用 pre[pos] + v[i].first 更新答案。
  5. 输出最终答案 ans(初始为 0)。

题解

#include <bits/stdc++.h>
using namespace std;

bool cmp(pair<int, int> x, pair<int, int> y)
{
	return x.second < y.second;
}

int getTwo(vector<pair<int, int>> items, int budget) 
{
	vector<pair<int, int>> v;
	for (auto p : items) 
	{
		if (p.second <= budget) v.push_back(p);
	}
	if (v.size() < 2) return -1;
	
	sort(v.begin(), v.end(), cmp);
	
	int m = v.size();
	vector<int> pre(m);
	pre[0] = v[0].first;
	for (int i = 1; i < m; i ++) 
	{
		pre[i] = max(pre[i - 1], v[i].first);
	}
	
	int ans = -1;
	for (int i = 1; i < m; i ++) 
	{
		int remain = budget - v[i].second;
		if (remain <= 0) continue;
		
		int l = 0, r = i - 1, pos = -1;
		while (l <= r) 
		{
			int mid = (l + r) / 2;
			if (v[mid].second <= remain) 
			{
				pos = mid;
				l = mid + 1;
			} 
			else 
			{
				r = mid - 1;
			}
		}
		if (pos != -1) 
		{
			ans = max(ans, pre[pos] + v[i].first);
		}
	}
	return ans;
}

int main() 
{
	int n, c, d;
	cin >> n >> c >> d;
	
	vector<pair<int, int>> coin, dia;
	for (int i = 0; i < n; i ++) 
	{
		int b, p; char type;
		cin >> b >> p >> type;
		if (type == 'C') coin.push_back({b, p});
		else dia.push_back({b, p});
	}
	
	int ans = 0;
	
	int bestC = -1, bestD = -1;
	for (auto it : coin)
	{
		if (it.second <= c) 
		{
			bestC = max(bestC, it.first);
		}
	}
	for (auto it : dia)
	{
		if (it.second <= d)
		{
			bestD = max(bestD, it.first);
		}
	}
		
	if (bestC != -1 && bestD != -1) ans = max(ans, bestC + bestD);
	
	int twoC = getTwo(coin, c);
	if (twoC != -1) ans = max(ans, twoC);
	
	int twoD = getTwo(dia, d);
	if (twoD != -1) ans = max(ans, twoD);
	
	cout << ans;
	return 0;
}
15 次阅读

评论

0