ecjtu-2020训练赛(1)解题报告

A题 CF1382A Common Subsequence

思路:
因为要求两个数组中的最短的公共子序列,如果有公共子序列的话,一定是1最短。也就是说只要同一个数即在a数组出现过,又在b数组中出现过,那么答案就是1,如果a数组和b数组中的任何一个数都不相同,就没有公共子序列,输出NO!

const int N = 1e3+5;

int n, m, a, b;
bool vis[N];

void solve()
{
     
	memset(vis, 0, sizeof vis);
	
	cin >> n >> m;
	
	int flag = 0, res;
	for(int i = 1; i <= n; i++)
	{
     
		cin >> a;
		vis[a] = true;	
	}
	for(int i = 1; i <= m; i++)
	{
     
		cin >> b;
		if(vis[b])	flag = 1, res = b;
	}
	
	if(flag)
	{
     
		cout << "YES" << endl << "1 " << res << endl;
	}
	else	cout << "NO" << endl;
}

int main()
{
     
	IOS;
	int t; cin >> t;
	while(t--)	solve();
	return 0;
}

B题 Sequential Nim

思路:
先尝试着推一下:

当只有一堆石子时:
很明显,First胜

当有两堆石子时:
如果第一堆的石子数a>1,那么First可以拿a-1个,留下1个给Second拿,接下来First又回到了只有一堆石子的必胜状态,First胜。
如果第一堆的石子数a=1,那么Second胜

当有三堆石子时:
且先不看第一堆,因为是博弈游戏,根据后两堆石子的数量,一定可以得出是先手会赢还是后手会赢。那么如果第三堆的石子数a>1,那么先手此时可以扭转局面——他可以让自己成为后手或者继续做先手。比如说他拿a-1和石子,那么到下一轮他又成为了下一堆的先手,而如果他拿完a个石子,那么到了拿下一堆的时候他成为了后手。

类似的,当有n堆石子时,只要两者中的某一个人遇到了(注意是第一次遇到时)石子数a>1的情况,那么他就可以根据后面剩余石子的数量来判断是先手能赢还是后手能赢,以此来决定自己是成为先手还是后手,此时游戏的最终获胜者就确定了。


void solve()
{
     
	int n; cin >> n;
	
	int a;
	bool flag = 1, find = 0;
	for(int i = 1; i <= n; i++)
	{
     
		cin >> a;
		if(find || i == n)	continue; // 如果已经遇到了石子数>1的情况 或者 当前已经是最后一堆石子了
		if(a > 1)	find = 1; // 遇到a>1,标记一下
		else	flag ^= 1; //在没遇到a>1之前,两者交替执行,1代表First,0代表Second
	}
	if(flag)	cout << "First" << endl;
	else	cout << "Second" <<endl;
}

int main()
{
     
	IOS;
	int t; cin >> t;
	while(t--)	solve();
	return 0;
}

C题 CF1385A Three Pairwise Maximums

这个就不多说了,按x,y,z的大小关系分类讨论,然后根据式子推一下就好了

可以分为3种情况:
① x > y

可得b>a且b>c
所以z = x,如果题目给的z不等于x就无解
最后令 a = y, b = x, c = y;

② x < y

和①类似
可得c>a且c>b
所以 z = y, 如果题目给的z不等于y就无解
最后令 a = x, b = x, c = y;

③ x = y

如果x = z ,说明3者相同,直接输出 a = x, b = x, c = x;
如果x < z,会出现矛盾,xa且c>b,但是我们的前提条件是x = y,这样会使的x !=y
如果x > z,输出 a = x, b = z, c = z;


D题 CF1385B Restore the Permutation by Merger

题目要求p数组中每一个数在原数组中的相对位置不变,且题目保证了有解,也就是说每个数都会出现2次,我们在读入a数组记住每个数是不是第一次出现,如果是,在p数组直接中加入它

const int N = 110;

vector<int> p;
bool vis[N];

void solve()
{
     
	p.clear(); memset(vis, 0, sizeof vis);
	
	int n; cin >> n;
	
	for(int i = 1, x; i <= 2*n; i++)
	{
     
		cin >> x;
		if(vis[x])	continue;
		vis[x] = true;
		p.push_back(x);
	}
	
	for(int i = 0; i < p.size(); i++)	cout << p[i] << " ";
	cout << endl;
}

int main()
{
     
	IOS;
	int t = 1; cin >> t;
	while(t--)	solve();
	return 0;
}

E题 CF1388A Captain Flint and Crew Recruitment

题目问正整数n是否可以写成四个互不相同的正整数的和,并满足这四个正整数中至少有三个是类质数。
我们可以求出前几个类质数
2×3 = 6
2×5 = 10
2 × 7 = 14
3 × 5 = 15

因为最少要三个,我们可以求出最小的一个满足条件的数为 6 + 10 + 14 + 1 = 31,如果给出的数比31还小,那么一定无法组成了。

题目说至少三个类质数,第四个可以是也可以不是。
我们可以直接让组成n的三个类质数就是 6,10,14, 然后第4个等于n-6-10-14,因为第四个可以是类质数也可以不是,那么我们得到的这个数无论如何都是满足条件的,除非它和前面的某一个数重复了,因为不能有重复数字出现,这个时候只需要将14换成15然后第四个数-1就不会重复了

const int N = 2e5 + 5;

int limit = 31;

void solve()
{
     
	int n; cin >> n;
	if(n < limit)	cout << "NO" <<endl;
	else
	{
     
		cout <<"YES" << endl;
		if(!(n - 30 == 6 || n - 30 == 10 || n - 30 == 14))
		{
     
			cout << 6 << " " << 10 << " " << 14 << " " << n - 30 << endl;
		}
		else
		{
     
			cout << 6 << " " << 10 << " " << 15 << " " << n - 31 << endl;
		}
	
	}
	
}

int main()
{
     
	IOS;
	
	int t = 1; cin >> t;
	while(t--)	solve();
	return 0;
}

F题 CF1388B Captain Flint and a Long Voyage

要求r最大时x最小
先来看如何r才能最大?
很明显,对于一个数,如果位数越多,那么他就越大,所以我们首先要保证去掉k后面的n位二进制数后,留下的位数最大。那么对于n的每一位,我们只能要么填9,要么填8,因为8和9的二进制都有四位

又因为在保证r最大的前提下x最小,因为后面的n位要去掉,所以只要和后边的n位有交集的都填8,其余的填9

void solve()
{
     
	int n; cin >> n;
	
	int pos = 1;
	while(4 * pos < n)	pos ++; // 找到8和9的分界线
	
	for(int i = n; i > pos; i--)	cout << 9;
	for(int i = pos ; i; i--)	cout << 8;
	cout << endl;
}

int main()
{
     
	IOS;
	int t; cin >> t;
	while(t--)	solve();
	return 0;
}

你可能感兴趣的