cf908(div2)题解(补题)

发布时间 2023-11-09 09:43:45作者: NOTHINGBUTNOTHING

纪念这次div2让我上绿名,但还是有点遗憾,差一点就可以过三题超神了

比赛链接cf908div2

A

这题是个骗人题,整个比赛会停下来就是一个人赢够了回合数,那么在谁这停下来就是谁赢了整个比赛,不用管每回合赢得规则。

#include<iostream>
using namespace std;
#include<string>
int main()
{
	int t;
	cin >> t;

	while (t--)
	{
		int n;
		cin >> n;
		string s;
		cin >> s;

		int A = 0, B = 0;
		for (int i = 0; i < s.size(); i++)
		{
			if (s[i] == 'A')
			{
				A++;
			}
			else
			{
				B++;
			}
		}
		if (s[s.size() - 1] == 'A')cout << "A" << endl;
		else cout << "B" << endl;

	}
	return 0;

}

B

先从最简单的情况考虑,或者说分析一下给的三个条件必须正好满足两个是什么意思
如果序列为一个数a重复三次,那么要满足任意两个条件,都不可避免的同时满足了三个条件
那么自然想到,要将两个条件分配给两个数,让他们分别去实现其中一个条件,比如,有两个5,两个6,那么就可以给两个5分别赋值为1,2,给两个6分别赋值为1 3
那么,我们可以先明确不满足的情况是这样的:出现次数大于等于2的数字小于两个。
而如果出现次数大于等于2的数字大于两个,我们就可以任意找两个数字,找到他们的两次出现位置,分别赋值为1,2,和1,3,然后其他所有位置都是1,就一定满足。

#include<iostream>
#include<unordered_map>
#include<queue>
using namespace std;
const int maxn = 100020;
#include<vector>

int main()
{
	int t;
	cin >> t;

	while (t--)
	{
		int a[maxn] = { 0 };
		int b[maxn] = { 0 };
		
		int n;
		cin >> n;
		
		unordered_map<int, int>mp;                //记录每个数出现了多少次
		int count_2 = 0;                          //记录有多少个数出现次数大于等于2
		unordered_map<int, int>asdf;
		for (int i = 1; i <= n; i++)
		{
			cin >> a[i];
			mp[a[i]]++;
			
		}
		for (int i = 1; i <= n; i++)
		{
			if (mp[a[i]] >= 2&&asdf.count(a[i])==0)
			{
				count_2++;
			
			}
			asdf[a[i]]++;
		}

		if (count_2 < 2)
		{
			cout << -1 << endl;
		}
		else
		{
			int firstnum = -1, secondnum = -1;
			int index = -1;
			for (int i = 1; i <= n; i++)
			{
				if (mp[a[i]] >= 2)
				{
					firstnum = a[i];
					index = i;
					break;
				}
			}
			for (int i = index + 1; i <= n; i++)
			{
				if (mp[a[i]] >= 2&&a[i]!=firstnum)
				{
					secondnum = a[i];
					break;
				}
			}

			int countfirst = 0, countsecond = 0;

			for (int i = 1; i <= n; i++)
			{
				if (a[i] == firstnum)
				{
					countfirst++;
					b[i] = countfirst;
					if (countfirst == 2)
					{
						break;
					}
				}
			}
			for (int i = 1; i <= n; i++)
			{
				if (a[i] == secondnum)
				{
					countsecond++;
					b[i] = countsecond;
					if (countsecond == 2)
					{
						b[i] = 3;
						break;
					}
				}
			}

			for (int i = 1; i <= n; i++)
			{
				if (b[i] == 0)
				{
					b[i] = 1;
				}

			}
			for (int i = 1; i <= n; i++)
			{
				cout << b[i] << " ";

			}
			cout << endl;
		}
		

		
	}

	return 0;

}

C

这题手动模拟几次可以发现,如果给的那个最终序列最后一个位置是x,那么它的上一个序列其实就是这个序列的后x位移动到序列前面,给定最终序列,那么如此反向操作k次,结果是唯一的。
如果中间发现这个最后一个数x>n,那么就说明不可能。
如果我们想模拟这个反向操作,可以把序列复制很多次,也可以用模运算,具体见代码:

#include<iostream>
using namespace std;
const int maxn = 200200;
#include<unordered_map>
#include<string>
#include<cstring>
long long a[maxn];
int main()
{
	int t;
	cin >> t;
	int tt = t;
	while (t--)
	{
		memset(a, 0, sizeof a);
		long long n, k;
		cin >> n >> k;
		
		for (int i = 0; i <n; i++)
		{
			cin >> a[i];
		}

		int flag = 1;
	    long long count = 0;
		unordered_map<long long, long long >num;
		for (long long i = n - 1; count <k;i=((i-a[i])+n)%n)           //count<k而不是<=k,因为要求是正好k次
		{
			if (a[i]>n)
			{
				
				flag = 0;
				break;
			}
			count++;
			if (num.count(i) != 0)                                     //遇到了重复下标,说明进入一个循环的过程了,就一定可以
			{
				flag = 1;
				break;
			}
			num[i]++;
		}
		if (flag == 1)cout << "Yes" << endl;
		else if(flag==0)cout << "No" << endl;

	}

	return 0;
}

分割线,赛时就想出来了这三道题,C题还因为<=k这个愚蠢bug没调出来,后面是补题,待更新