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题解都看不懂,走了走了。