Codeforces Round #804 (Div. 2) A B C D
A.The Third Three Number Problem
题意: 判断是否有 3 3 3 个数 a , b , c a,b,c a,b,c 使得 a a a ^ b b b + b b b ^ c c c + c c c ^ a = n a = n a=n 。
思路: 首先看样例,发现 n = 1 n=1 n=1 的情况下答案为 − 1 -1 −1 ,其余情况 n n n 皆为偶数且有值 , 猜想 n n n 为奇数情况答案为 − 1 -1 −1 。证明: 3 3 3 个数 a , b , c a,b,c a,b,c 二进制形式下最后一位的取值有 000 000 000、 001 001 001、 011 011 011、 111 111 111 四种情况,可以发现任意一种情况都不能构成 奇数和 ,所以如果 n n n是奇数,那么值必然为 − 1 -1 −1。如果是偶数我们直接构造 0 0 0 0 0 0 n / 2 n/2 n/2 即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
//Edit Author: Snow
void cf(){
int n;
cin>>n;
if(n%2){
cout<<"-1"<<endl;
}
else{
cout<<0<<' '<<0<<' '<<n/2<<endl;
}
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
while(t--){
cf();
}
}
B.Almost Ternary Matrix
题意: 给你一个 n ∗ m n * m n∗m 的矩阵且 n n n 和 m m m 都是 偶数 ,问如果构造一个 01 01 01 矩阵,使得每个元素有且仅有两个对立元素存在。( 1 1 1的对立是 0 0 0, 0 0 0的对立是 1 1 1)。
思路: 面向样例编程题,通过 例 1 1 1 样例结合 例 3 3 3 样例很容易发现一种构造方法。如 例 3 3 3 如果将其构造成如下:
- 1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1
那么这道题就一眼得知如何构造了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
//Edit Author: Snow
const int N = 55;
int a[N][N];
void cf(){
int n,m;
cin>>n>>m;
n/=2;
m/=2;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++){
if(i%2&&j%2){
a[i*2+1][j*2+1]=1;
a[i*2+1][j*2+2]=0;
a[i*2+2][j*2+1]=0;
a[i*2+2][j*2+2]=1;
}
else if(i%2&&j%2==0){
a[i*2+1][j*2+1]=0;
a[i*2+1][j*2+2]=1;
a[i*2+2][j*2+1]=1;
a[i*2+2][j*2+2]=0;
}
else if(i%2==0&&j%2){
a[i*2+1][j*2+1]=0;
a[i*2+1][j*2+2]=1;
a[i*2+2][j*2+1]=1;
a[i*2+2][j*2+2]=0;
}
else if(i%2==0&&j%2==0){
a[i*2+1][j*2+1]=1;
a[i*2+1][j*2+2]=0;
a[i*2+2][j*2+1]=0;
a[i*2+2][j*2+2]=1;
}
}
for(int i=1;i<=n*2;i++){
for(int j=1;j<=m*2;j++)cout<<a[i][j]<<' ';
cout<<endl;
}
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
while(t--){
cf();
}
}
C.The Third Problem
题意: 给定一组 0 0 0 ~ n − 1 n-1 n−1 的排列 A A A 。问:能构造多少组排列 B B B ,使得 A A A 相似 B B B 。 相似的定义是对于在 A A A 和 B B B 中任意的 l , r l,r l,r 都有 A . M E X ( l , r ) = B . M E X ( l , r ) A.MEX(l,r)=B.MEX(l,r) A.MEX(l,r)=B.MEX(l,r) ,问一共多多少种可能,答案对 1 e 9 + 7 1e9+7 1e9+7 取模。
思路: 根据 M E X MEX MEX的性质:第一个不存在于区间中的最小非负数。我们由小到大去寻找,首先是 0 0 0 的位置,必然是固定不动,否则 0 0 0 原先在的位置 M E X MEX MEX 就不再为 1 1 1。其次是 1 1 1 , 1 1 1 的位置也必然不动,否则以 01 01 01 为区间两端的 M E X MEX MEX会造成影响。接下去找 2 2 2 的位置,如果 2 2 2 出现在 01 01 01 区间内,那么发现 2 2 2无论在区间内的哪个位置都是有效位置,所以总方案数乘上剩余有效位置,即 ( r − l + 1 ) − 2 ( 0 和 1 的 位 置 ) (r-l+1)-2(0和1的位置) (r−l+1)−2(0和1的位置) 。如果不在 01 01 01 区间内类似 01 01 01 之间的关系, 3 3 3 也必然固定不动,扩展 l , r l,r l,r的区间范围。那么得知正解即为 f o r for for 一遍 0 0 0 ~ n − 1 n-1 n−1的位置实时扩展 l , r l,r l,r 区间且更新最终的方案数。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
//Edit Author: Snow
const int N = 1e5+10;
int a[N];
int idx[N];
const int mod=1e9+7;
void cf(){
int n;
cin>>n;
int l,r;
for(int i=1;i<=n;i++){
cin>>a[i];
idx[a[i]]=i;
if(a[i]==0)l=r=i;
}
int res=1;
for(int i=1;i<n;i++){
if(idx[i]<l||idx[i]>r){
l=min(l,idx[i]);
r=max(r,idx[i]);
}
else{
res=res*(r-l+1-i)%mod;
}
}
cout<<res<<endl;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
while(t--){
cf();
}
}
D.Almost Triple Deletions
题意: 给定一组数组,你可以通过任意次可以为 0 0 0 次操作:
- 选择两个相邻且不相等的数并删除这两个数
求最终可以得到的长度最大的且元素全部相同的数组长度为多长。
思路: 首先我们从最终情况进行考虑,最终剩余的必然空数组或者是一连串相等的数。那么假设这一连串相等的数是
3
3
3 而原数组中第
1
1
1 个数假如不是
3
3
3 那么意味着我们需要找到从第
1
1
1 位开始的一段 可以删除的区间 使得区间删除之后数组的第
1
1
1 位是
3
3
3。由于复杂度支持
O
(
n
2
)
O(n^2)
O(n2) ,这给我们一个思路,这题可以先将可删除的区间预处理出来。区间可删除的条件易得有:
1
:
1:
1:区间长度为偶数。
2
:
2:
2:区间内最多数量的数不超过总长度的一半。
那么我们接下去就可以
O
(
1
)
O(1)
O(1) 判断一段区间是否可删除。我们可以预处理位置
1
1
1 -
n
n
n ,如果
0
0
0 -
i
−
1
i - 1
i−1 是可以删除的,那么说明当前位置作为开头是合理的,使
d
p
[
i
]
=
1
dp[i]=1
dp[i]=1。
接下去本题就可以转化成一种类 LIS 做法,判断枚举到第
j
j
j 个位置时,当前位置的数与前面同样出现过的相同数之间进行一个更新,如果前面出现相同数的下标
d
p
[
i
]
>
0
dp[i]>0
dp[i]>0 说明是合理的并且需要同样满足区间
[
i
+
1
,
j
−
1
]
[i+1,j-1]
[i+1,j−1] 可删除,如果
i
i
i 和
j
j
j 相邻需要特判一下,如果都满足那么
d
p
[
j
]
=
m
a
x
(
d
p
[
j
]
,
d
p
[
i
]
+
1
)
dp[j]=max(dp[j],dp[i]+1)
dp[j]=max(dp[j],dp[i]+1) ,复杂度
O
(
n
2
)
O(n^2)
O(n2)。那么最终的
d
p
[
n
]
dp[n]
dp[n] 就是以
s
[
i
]
s[i]
s[i] 中的字符为最终连续串所能获得的最大值。但
s
[
n
]
s[n]
s[n] 不一定为最长连续串的字符。所以我们还需要维护一个
m
a
ma
ma ,如果枚举到
j
j
j 位置时,区间
[
j
+
1
,
n
]
[j+1,n]
[j+1,n] 可删除,那么我们更新一下的
m
a
ma
ma 。最终的
m
a
ma
ma 即为最长连续串。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
//Edit Author: Snow
const int N = 5010;
int a[N];
int dp[N];
void cf(){
int n;
cin>>n;
int delete_table[n+1][n+1]={0};
for(int i=1;i<=n;i++)cin>>a[i];
//预处理删除表
for(int i=1;i<=n;i++){
int cnt[n+1]={0};
int ma=0;
for(int j=i;j<=n;j++){
cnt[a[j]]++;
ma=max(ma,cnt[a[j]]);
if(ma*2<=j-i+1&&(j-i+1)%2==0){
delete_table[i][j]=1;
}
}
}
memset(dp,0,sizeof dp);
dp[1]=1;
for(int i=2;i<=n;i++){
if(delete_table[1][i-1])dp[i]=1;
}
int ma=0;
for(int i=1;i<=n;i++){
for(int j=1;j<i;j++){
if(j+1==i&&a[i]==a[j]&&dp[j]){
dp[i]=dp[j]+1;
}
else{
if(a[j]==a[i]&&delete_table[j+1][i-1]==1&&dp[j]){
dp[i]=max(dp[i],dp[j]+1);
}
}
}
if(i==n)ma=max(ma,dp[i]);
else if(delete_table[i+1][n])ma=max(ma,dp[i]);
}
cout<<ma<<endl;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
while(t--){
cf();
}
}