Codeforces Round #658 (Div. 2)

A Common Subsequence

只要找到有一个相同的元素输出即可。

AC代码:

const int N = 1010;
int a[N], b[N];
int ans;
int cnt[N];
int main()
{
	int t;
	sd(t);
	while (t--)
	{
		int n, m, k;
		bool flag = 0;
		sdd(n, m);
		mem(cnt, 0);
		rep(i, 1, n)
		{
			sd(a[i]);
			cnt[a[i]]++;
		}
		rep(i, 1, m)
		{
			sd(b[i]);
			if (cnt[b[i]])
			{
				flag = 1;
				k = 1;
				ans = b[i];
			}
		}
		if (flag)
		{
			puts("YES");
			pdd(k, ans);
		}
		else
			puts("NO");
	}
	return 0;
}

B Sequential Nim

题意:

N N N 个石子堆,每个石子堆有 a [ i ] a[i] a[i] 个石子,现在从第一堆开始往后取,谁最先没法取石子,谁就输了,问最后谁先手还是后手赢了?

这个博弈明显的先手必胜态,因为先手拿的话可以每次都把每堆拿剩下一个,然后下一个拿只能拿最后一个,然后到了最后一堆直接拿完。

考虑一个特殊情况那就是中间出现 1 1 1 的情况,比如:

. . . 4 , 5 , 1 , 3 ...4,5,1,3 ...4,5,1,3这种情况那就在 4 4 4 这一堆拿三个,然后在 5 5 5 这一堆拿 5 5 5 个,然后后手只能拿 1 1 1 拿完,这样先手下一次就可以直接拿完,还是先手必胜态。

考虑开头连续 1 1 1 的个数,如果奇数个 1 1 1 那么先手变后手,后手变先手。

再对全为 1 1 1 进行特判。

AC代码:

int main()
{
	int t;
	sd(t);
	while (t--)
	{
		int n, m, k;
		int cnt = 0;
		sd(n);
		rep(i, 1, n)
			sd(a[i]);
		rep(i, 1, n)
		{
			if (a[i] == 1)
				cnt++;
			else
				break;
		}
		if (cnt == n)
		{
			if (cnt & 1)
				puts("First");
			else
				puts("Second");
		}
		else
		{
			if (cnt & 1)
				puts("Second");
			else
				puts("First");
		}
	}
	return 0;
}

C Prefix Flip

题意:

一种操作,可以选择前缀长度为 k k k ,翻转前 k k k 01 01 01 值然后顺序再倒一下。要求在 2 n 2n 2n 步内把给定 a a a 串变成 b b b 串。
输出任意一种方案数。

既然是任意一种那就找罪容易的一种,每一个都操作,从最后一位往前开始考虑,如果 b b b 串这个位置和 a a a 串 的第一位相同那么就把 a a a 串 的第一位个翻转一下,然后再把当前位置的前缀都翻转,这样这一位就符合了。为了降低复杂度直接记录, a 1 a_1 a1在原序列中跳跃是这样的 p o s = : 1 , n , 2 , n − 1 , 3 … pos=: 1 ,n ,2, n-1, 3 … pos=:1,n,2,n1,3一直到 n 2 \frac{n}{2} 2n

举个例子:
1 , 2 , 3 , 4 , 5 1,2,3,4,5 1,2,3,4,5

5 , 4 , 3 , 2 , 1 5,4,3,2,1 5,4,3,2,1

2 , 3 , 4 , 5 , 1 2,3,4,5,1 2,3,4,5,1

4 , 3 , 2 , 5 , 1 4,3,2,5,1 4,3,2,5,1

3 , 4 , 2 , 5 , 1 3,4,2,5,1 3,4,2,5,1

AC代码:

const int N = 1e5 + 50;
int n;
char s1[N], s2[N];
int cnt, res, pos;
vector<int> v;

int main()
{
	int t;
	sd(t);
	while (t--)
	{
		sd(n);
		ss(s1 + 1);
		ss(s2 + 1);
		v.clear();
		cnt = n;
		res = 0;
		pos = 1;
		per(i, n, 1)
		{
			//pddd(s2[i] - '0',res,(s2[i] - '0') ^ res);
			if ((s1[pos] - '0') == ((s2[i] - '0') ^ res)) //每次都翻转前缀和为 n-- 的 这个操作就是代替了翻转的操作
				v.pb(1);
			v.pb(cnt--);
			pos = n - pos + 1;//翻转后的位置.
			if (pos <= n / 2)
				pos++;
			res = 1 - res; //0,1切换
		}
		printf("%d ", v.size());
		for (auto i : v)
			printf("%d ", i);
		printf("\n");
	}
	return 0;
}

D Unmerge

题意:

给两个数组的 m e r g merg merg 规则,给一个 2 n 2n 2n 的序列 p p p ,问是不是两个数组 m e r g e merge merge 之后的结果。 这个 m e r g e merge merge 规则举个例子:

a = 3 , 2 , 8 , 4 a=3,2,8,4 a=3,2,8,4

b = 6 , 1 , 5 , 7 b=6,1,5,7 b=6,1,5,7

a 1 < b 1 a_1a1<b1 所以先取 a 1 , 3 a_1,3 a1,3

a 2 < b 1 a_2a2<b1 a 2 , 2 a_2,2 a2,2

a 3 > b 1 a_3>b_1 a3>b1 b 1 , 6 b_1,6 b1,6

a 3 > b 2 a_3>b_2 a3>b2 b 2 , 1 b_2,1 b2,1

a 3 > b 3 a_3>b_3 a3>b3 b 3 , 5 b_3,5 b3,5

a 3 > b 4 a_3>b_4 a3>b4 b 4 , 7 b_4,7 b4,7

最后取 a 3 , a 4 , 8 , 4 a_3,a_4,8,4 a3,a48,4

组成序列 32615784 3 2 6 1 5 7 8 4 32615784

考虑第一个数 a [ 1 ] a[1] a[1],则向右第一个比 a [ 1 ] a[1] a[1] 大的数为 b [ 1 ] b[1] b[1]

b [ 1 ] b[1] b[1] 也是这样考虑,找右边第一个比他大的数,放在一块。

分组以后用 01 01 01 背包判断是否可以刚刚分组。

AC代码:

const int N = 4e5 + 50;
int n, k, m;
int a[N];
vector<int> ans;
int dp[N];
int tmp, cnt;
int main()
{
	int t;
	sd(t);
	while (t--)
	{
		mem(dp, 0);
		ans.clear();
		sd(n);
		rep(i, 1, 2 * n)
			sd(a[i]);
		tmp = a[1];
		cnt = 1;
		rep(i, 2, 2 * n)
		{
			if (a[i] < tmp)
				cnt++;
			else if (a[i] > tmp)
			{
				ans.pb(cnt);
				cnt = 1;
				tmp = a[i];
			}
		}
		ans.pb(cnt);
		sort(ans.begin(), ans.end());
		rep(i, 0, ans.size() - 1)
		{
			per(j, n, ans[i])
				dp[j] = max(dp[j], dp[j - ans[i]] + ans[i]);
		}
		if (dp[n] == n)
			puts("YES"); //刚好装满
		else
			puts("NO");
	}
	return 0;
}

你可能感兴趣的:(CodeForces)