Codeforces Round 814 (Div. 2)

发布时间 2023-12-14 11:39:35作者: 加固文明幻景

基本情况

又是过了ABC。

A、B思路更多的是从数据上分析出来的,过的很顺。

C经典拿评测机来调试,甚至还RE了一次,最后终于过了。

C. Fighting Tournament

Problem - C - Codeforces

第一次改错

这题我的思路是找到规律后,优先队列加二分查找。

但是一直WA第二个点,这是我一开始的代码:

void solve()
{
	int n, Q, cnt = 0; std::cin >> n >> Q;
	int maxx = -1;
	for (int i = 1; i <= n; i++) std::cin >> a[i], maxx = std::max(maxx, a[i]);
	std::priority_queue<int,std::vector<int>,std::greater<int>> q;
	q.push(a[1]);
	for (int i = 2; i <= n; i++)
	{
		cnt++;
		q.push(a[i]); 
		tag[q.top()].push_back(cnt);
		q.pop();
		if (q.top() == maxx) break;	
	}
	while(Q--)
	{
		int u, k; std::cin >> u >> k;
		if (k >= cnt)
		{
			if (a[u] == maxx) std::cout << tag[a[u]].size() + k - cnt + 1;
			else std::cout << tag[a[u]].size() ;
		}	
		else
		{
			auto p = std::lower_bound(tag[a[u]].begin(), tag[a[u]].end(), k);
			std::cout << (p - tag[a[u]].begin()) + 1;
		}
		std::cout << std::endl;
	}
}

可以说是漏洞满篇,第一次改错我发现了一下几个问题(可恶的是样例都没卡掉)

  • tag[a[u]].size() + k - cnt

    • 这个结果是可能爆 int
    • 前面加上 long long 强转
  • tag[q.top()].push_back(cnt);

    • 这个点是我造了一组数据才发现的,(样例到底怎么过的?)。

    • 经典胡言乱语,我这里开的是小优先的优先队列,明明队首是小的,而这里想要的是记录胜者的场次。

    • 语句调换一下,先 pop 再剩下的 top 就是最大的了。

    • q.push(a[i]); q.pop();
      tag[q.top()].push_back(cnt);
      

然而提交后还是WA on test 2

第二次改错

void solve()
{
	int n, Q; std::cin >> n >> Q;
	long long cnt = 0;
	int maxx = -1;
	for (int i = 1; i <= n; i++) std::cin >> a[i], maxx = std::max(maxx, a[i]), tag[i].clear();
	std::priority_queue<int,std::vector<int>,std::greater<int>> q;
	q.push(a[1]); 
	for (int i = 2; i <= n; i++)
	{
		cnt++;
		q.push(a[i]); q.pop();
		tag[q.top()].push_back(cnt);
		if (q.top() == maxx) break;	
	}
	while(Q--)
	{
		int u, k; std::cin >> u >> k;
		if (k >= cnt)
		{
			if (a[u] == maxx) std::cout << (long long) tag[a[u]].size() + k - cnt;
			else std::cout << tag[a[u]].size() ;
		}	
		else
		{
			auto p = std::lower_bound(tag[a[u]].begin(), tag[a[u]].end(), k);
			std::cout << (long long) (p - tag[a[u]].begin()) + 1;
		}
		std::cout << std::endl;
	}
}

还是通过造数据找到的问题。

lower_bound 我下意识的认为是找第一个小于等于 \(k\) 的位置了,(什么根据题目自适应),但实际上是找第一个大于等于 \(k\) 的位置啊!所以整个 else 都是错的,再改。

else
{
	auto p = std::lower_bound(tag[a[u]].begin(), tag[a[u]].end(), k);
	if (*p == k) std::cout << (long long) (p - tag[a[u]].begin()) + 1;
	else
	{
		if (p == tag[a[u]].begin()) std::cout << 0;
		else 
		{
			p--;
			std::cout << (long long) (p - tag[a[u]].begin()) + 1;
		}
	}
}

然后RE了。

第三次改错

这个时候思路就比较清晰了,因为RE大概率就是迭代器操作的问题,然后我就发现要是没找到怎么办

比如我要查询的数据是第 \(7\) 轮,总共比了 \(9\) 轮,所以不会被上面那个 if 处理,而是转到了 else

然而查询的这个人最后赢得是第 \(5\) 轮,那么此时 lower_bound 是找不到第一个大于等于 7 的数的,它会返回 tag[a[u]].end()

其实到目前为止也没问题,因为我下面的算法已经让 p 自减了,算出来的正确性是可以保证的。

但是,我有一个语句是 if(*p == k) 而这里当 p = tag[a[u]].end() 的时候,显然会访问一个不确定的指针指向的值,这就导致了RE.

我是直接在这条语句上面加了特判来规避了RE,终于AC。

void solve()
{
	int n, Q; std::cin >> n >> Q;
	long long cnt = 0;
	int maxx = -1;
	for (int i = 1; i <= n; i++) std::cin >> a[i], maxx = std::max(maxx, a[i]), tag[i].clear();
	std::priority_queue<int,std::vector<int>,std::greater<int>> q;
	q.push(a[1]); 
	for (int i = 2; i <= n; i++)
	{
		cnt++;
		q.push(a[i]); q.pop();
		tag[q.top()].push_back(cnt);
		if (q.top() == maxx) break;	
	}
	while(Q--)
	{
		long long u, k; 
		std::cin >> u >> k;
		if (k >= cnt)
		{
			if (a[u] == maxx) std::cout << (long long) tag[a[u]].size() + k - cnt;
			else std::cout << tag[a[u]].size() ;
		}	
		else
		{
			auto p = std::lower_bound(tag[a[u]].begin(), tag[a[u]].end(), k);
			if (p == tag[a[u]].end()) std::cout << tag[a[u]].size();
			else if (*p == k) std::cout << (long long) (p - tag[a[u]].begin()) + 1;
			else
			{
				if (p == tag[a[u]].begin()) std::cout << 0;
				else 
				{
					p--;
					std::cout << (long long) (p - tag[a[u]].begin()) + 1;
				}
			}
		}
		std::cout << std::endl;
	}
}