Codeforces Round #804 (CF1699)

来进行一个博客的水()

22:34,打开codeforces!转圈半天,看A,完全没思路,什么东西。

先写个结论验证,然后发现sublime的cmd寄了,非常生气,然后开始修,顺便把小号注册比赛。

修到一半发现一只蚊子停在了桌子角上!!!!我想拿电蚊拍未遂(怕他跑掉),于是手起刀落,成功干掉()

然后继续看A,验证了半天好像没有奇数,于是开写()

然后看B,水题切了。

然后看C,罚坐1h。。。



A. The Third Three Number Problem

给定一个数\(n\),求\(a, b, c\)使得\(n = (a \oplus b)+(a \oplus c)+(b \oplus c)\)


由@Lcyanstars指出:二进制第一位加和异或是一样的!所以\(n\)为奇数时无解。于是就做出来樂。


int n = next();
if (n%2) cout<<-1<<endl;
else cout<<0<<' '<<0<<' '<<n/2<<endl;


B. Almost Ternary Matrix

把矩阵每一个填成黑白颜色,要求每个格子都有2个邻居和自己异色。


淼淼淼。易得:

1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1

重复的时候成立。


rep(j, 0, n)
{
	for(int i = 0; i < m; i += 2)
	{
		if (j%4 == 0 || j%4 == 3) if (i%4) cout<<"0 1 "; else cout<<"1 0 ";
		else if (i%4) cout<<"1 0 "; else cout<<"0 1 ";
	}
	cout<<endl;
}


C. The Third Problem

一个排列\(a\),长度为\(n\)。定义两个排列相似,当且仅当\(\operatorname{MEX}([a_l, ..., a_r]) = \operatorname{MEX}([b_l, ..., b_r])\)对于任何\(l, r\)成立。求和\(a\)相似的排列个数?

其中\(\operatorname{MEX}\)为序列中没有的最小自然数。


在看这道题的时候,我首先从Example#4推了个结论:

在0到1之间的连续自然数,不能到0到1这个序列的外面去。

显然,若是有任何一个到了外面,\(\operatorname{MEX}\)都会改变。我们设0到1里面最大的连续自然数为k,又能发现k+1一定在0到1的外面而且也不能动!

于是我们可以把区间0到1扩展到(0或1)到k+1。在这里面最大的连续自然数也不能出(0或1)到k+1这个区间!

于是思路就明朗了:先以从0到1的区间为底,然后找出k+1,类似双指针一样把区间扩大,再重复以上操作直到区间覆盖了整个排列。

其中,\([2, k_0]\)可以在0到1的区间里任意排列,\([k_0+2, k_1]\)可以在(0或1)到k+1里任意排列,我们把这些排列的方案数相乘就得到了最终答案。


int n = next();
rep(i, 0, n) cin>>w[i], at[w[i]] = i;
if (n == 1) { cout<<1<<endl; continue; }

int l = min(at[0], at[1]), r = max(at[0], at[1]), ans = 1, t = 1;
while(1)
{
	int g = abs(r-l)-t, maxx = t+1;
	while(maxx < n && l < at[maxx] && at[maxx] < r) ans = ans*g%MOD, g--, maxx++;
	if (maxx == n) break;
	t = maxx; l = min(l, at[maxx]); r = max(r, at[maxx]);
}
cout<<ans<<endl;


D. Almost Triple Deletions

给出一个序列\(a\),你可以任意次删除两个相邻且不相等的数。求由\(a\)得到的所有元素都相等的序列的长度最大值。


可以发现这是道dp!

我们先来扩大题目:如果不要求删除的数不相等,怎么写?

当然可以写贪心,不过贪心缩小不到这道题上,于是我们开写dp()

易得:\(dp(i) = dp(j)+1 (i > j\ \&\&\ a_i = a_j\ \&\& (i-j-1)\%2 = 0)\)

其中状态设计为:\(dp(i)\)表示前i个数能保留下的元素都为\(a_i\)的序列的长度最大值。

然后我们把这个dp缩小到这道题上,也就是说,我们多了一个删除数要不相等的限制。

那么dp转移多了什么限制呢?就是[j+1, i-1]之间这个区间,必须能够完全删光!

怎么可以完全删光?这很好想:当这个区间的众数出现次数不大于区间长度的一半的时候,这个区间就可以删光樂!

这个区间能否被删光的处理可以用\(O(n^2)\)的时间复杂度解决。

此外,还有两个点。一个在于预处理:要注意\(dp(i)=1\)当且仅当\([0, i-1]\)这个区间也可以被删光。(下标从0开始)

另一个点在于从dp数组中获得答案。我们知道这个序列是要删光的,所以dp方程的中间结果都是不可信的。于是我们可以额外添加一个状态\(dp(n)\),从任何\(i\)转移到\(n\)不需要满足元素相等这个条件,只需要满足\([i+1, n-1]\)这个区间可以被删光就行了。


int T = next();
while(T--)
{
	ms(cnt, 0);

	int n = next(), maxx = 0;
	rep(i, 0, n) cin>>w[i];

	hrp(i, 0, n)
	{
		if (!(i%2) && maxx*2 <= i) dp[i] = 1;
		else dp[i] = 0;
		
		maxx = max(maxx, ++cnt[w[i]]);
	}
	
	rep(i, 0, n)
	{
		ms(cnt, 0); maxx = 0;
		
		if (dp[i]) hrp(j, i+1, n)
		{
			if ((w[i] == w[j] || j == n) && (j-i-1)%2 == 0 && maxx*2 <= j-i-1) dp[j] = max(dp[j], dp[i]+1);
			maxx = max(maxx, ++cnt[w[j]]);
		}
	}
	cout<<max(0LL, dp[n]-1)<<endl;
}

E题解都看不懂,走了走了。

posted @ 2022-07-05 21:36  Loxilante  阅读(43)  评论(0编辑  收藏  举报