视频讲解:BV1vN4y1M7cx
A. The Third Three Number Problem
题目大意
给定整数
n
(
1
≤
n
≤
1
0
9
)
n(1\leq n \leq 10^9)
n(1≤n≤109) ,求任意一组
a
,
b
,
c
(
0
≤
a
,
b
,
c
≤
1
0
9
)
a,b,c(0 \leq a,b,c \leq 10^9)
a,b,c(0≤a,b,c≤109) ,满足
(
a
⊕
b
)
+
(
b
⊕
c
)
+
(
a
⊕
c
)
=
n
(a \oplus b)+(b \oplus c)+(a \oplus c)=n
(a⊕b)+(b⊕c)+(a⊕c)=n
无解输出 − 1 -1 −1 。
题解
仅考虑最低位的情况,会发现:
- 若三个数都为 00 00 00 或 01 01 01 ,则总和为 00 00 00 ;
- 若一个数为 01 01 01 ,另两个数为 00 00 00 ,则总和为 10 10 10 ;
- 若一个数为 00 00 00 ,另两个数为 01 01 01 ,则总和为 10 10 10 ;
因此对于任意一位,要不总和为 0 0 0 ,要不向更高位进一位。
因此若
n
n
n 为奇数,则无解。
有解时,一种合法的方案为
(
n
2
,
0
,
0
)
(\frac{n}{2},0,0)
(2n,0,0) 。
参考代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int T,n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
if(n&1)
printf("-1\n");
else
printf("%d %d %d\n",n/2,0,0);
}
}
B. Almost Ternary Matrix
题目大意
给定两个偶数 n , m ( 2 ≤ n , m ≤ 50 ) n,m(2\leq n,m \leq 50) n,m(2≤n,m≤50) ,求 n × m n \times m n×m 的01矩阵,满足对于矩阵内任意位置 ( i , j ) (i,j) (i,j) ,在四邻域上均有恰好2个与该元素不同的元素。
题解
用以下矩阵及其旋转90°的矩阵填充即可:
01
10
参考代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=55;
int a[MAXN][MAXN];
int main()
{
int T,n,m,i,j;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
memset(a,0,sizeof(a));
for(i=0;i<n;i+=2)
{
for(j=0;j<m;j+=2)
{
if((i/2+j/2)%2)
a[i][j]=a[i+1][j+1]=1;
else
a[i+1][j]=a[i][j+1]=1;
}
}
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
printf("%d ",a[i][j]);
puts("");
}
}
}
C. The Third Problem
题目大意
给定长度为
n
n
n 的
1
1
1 到
n
−
1
n-1
n−1 的排列
a
a
a ,求有多少排列
b
b
b 与排列
a
a
a “相似”。
“相似”的定义为,对于任意区间
[
l
,
r
]
(
1
≤
l
≤
r
≤
n
)
[l,r](1 \leq l \leq r \leq n)
[l,r](1≤l≤r≤n) ,均有
M
E
X
(
[
a
l
,
a
l
+
1
,
.
.
.
,
a
r
]
)
=
M
E
X
(
[
b
l
,
b
l
+
1
,
.
.
.
,
b
r
]
)
MEX([a_l,a_{l+1},...,a_r])=MEX([b_l,b_{l+1},...,b_r])
MEX([al,al+1,...,ar])=MEX([bl,bl+1,...,br])
题解
根据
M
E
X
MEX
MEX 的定义,我们需要从小到大逐个考虑每个数。
设
p
o
s
i
pos_i
posi 表示排列
a
a
a 中元素
i
i
i 出现的位置。
考虑用类似数学归纳法的思想,求解排列 b b b 的方案数。
初始区间 [ p o s 0 , p o s 0 ] [pos_0,pos_0] [pos0,pos0] 的 M E X MEX MEX 值必须为 1 1 1 ,因此 0 0 0 位置确定,方案数为 1 1 1 。
设当前区间为
[
l
,
r
]
[l,r]
[l,r] ,
0
0
0 到
i
−
1
i-1
i−1 的元素均在改区间中。
对于元素
i
i
i :
- 若 p o s i ∈ [ l , r ] pos_i\in [l,r] posi∈[l,r] ,则在排列 b b b 中元素 i i i 也必须在 [ l , r ] [l,r] [l,r] 中,可选位置数为 r − l + 1 − i r-l+1-i r−l+1−i ,因此方案数 ∗ ( r − l + 1 − i ) *(r-l+1-i) ∗(r−l+1−i)。
- 若 p o s i ∉ [ l , r ] pos_i \notin [l,r] posi∈/[l,r] ,则在排列 b b b 中元素 i i i 必须在 p o s i pos_i posi 位置上,方案数不变,并且 [ l , r ] [l,r] [l,r] 扩展至 [ m i n ( l , p o s i ) , m a x ( r , p o s i ) ] [min(l,pos_i),max(r,pos_i)] [min(l,posi),max(r,posi)] 。
参考代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int MAXN=100100;
int pos[MAXN];
int main()
{
int T,n,x,i,l,r;
ll ans;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&x);
pos[x]=i;
}
l=r=pos[0];
ans=1;
for(i=1;i<n;i++)
{
if(l<=pos[i]&&pos[i]<=r)
ans=ans*(r-l+1-i)%mod;
else
{
l=min(l,pos[i]);
r=max(r,pos[i]);
}
}
printf("%lld\n",ans);
}
}
D. Almost Triple Deletions
题目大意
给定长度为 n ( 1 ≤ n ≤ 5000 ) n(1 \leq n \leq 5000) n(1≤n≤5000) 的数组 a a a 。进行若干次操作,每次操作可以选择一个 i ( 1 ≤ i < n ) i(1\leq i <n) i(1≤i<n) ,满足 a i ≠ a i + 1 a_i \neq a_{i+1} ai=ai+1 ,然后删去 a i a_i ai 和 a i + 1 a_{i+1} ai+1 。
以任意方式进行若干次操作,求最后剩下完全相同的元素时,数组最长可以是多少。
题解
考虑对于任意区间
[
l
,
r
]
[l,r]
[l,r] ,什么情况下能将其中的元素恰好完全删除。
满足以下两个条件时,方可:
- r − l + 1 r-l+1 r−l+1 为偶数
- 区间的众数出现次数,不超过 n 2 \frac{n}{2} 2n
通过预处理或枚举区间端点等方式,可以很快判断任意区间能否完全删除。
设
d
p
i
dp_i
dpi 表示在前
i
i
i 个元素中,最后剩下
a
i
a_i
ai 时,最多可以剩下多时少元素。若无法剩下
a
i
a_i
ai 则为负无穷大。初始
d
p
0
=
0
dp_0=0
dp0=0 。
则有转移式:
d
p
i
=
max
j
=
0
i
−
1
(
D
e
l
e
t
a
b
l
e
(
j
+
1
,
r
)
&
(
a
i
=
a
j
∣
j
=
0
)
)
∗
(
d
p
j
+
1
)
dp_i=\max_{j=0}^{i-1}(Deletable(j+1,r)\&(a_i=a_j|j=0))*(dp_j+1)
dpi=j=0maxi−1(Deletable(j+1,r)&(ai=aj∣j=0))∗(dpj+1)
两重循环枚举
i
,
j
i,j
i,j 即可。
枚举
j
j
j 时倒序枚举以便求解
D
e
l
e
t
a
b
l
e
(
j
+
1
,
r
)
Deletable(j+1,r)
Deletable(j+1,r) 。
最终答案为
a
n
s
=
max
i
=
1
n
D
e
l
e
t
a
b
l
e
(
i
+
1
,
n
)
∗
d
p
i
ans=\max_{i=1}^{n} Deletable(i+1,n)*dp_i
ans=i=1maxnDeletable(i+1,n)∗dpi
参考代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=5050;
const int inf=1<<30;
int a[MAXN],sum[MAXN],dp[MAXN];
int main()
{
int T,n,i,j,mxnum,ans;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
for(i=1;i<=n;i++)
{
memset(sum,0,sizeof(int)*(n+5));
dp[i]=-inf;
mxnum=0;
for(j=i-1;j>=0;j--)
{
if((i-j-1)%2==0&&mxnum<=(i-j-1)/2&&(j==0||a[i]==a[j]))
dp[i]=max(dp[i],dp[j]+1);
sum[a[j]]++;
mxnum=max(mxnum,sum[a[j]]);
}
}
memset(sum,0,sizeof(int)*(n+5));
ans=0;
mxnum=0;
for(i=n;i>=1;i--)
{
if((n-i)%2==0&&mxnum<=(n-i)/2)
ans=max(ans,dp[i]);
sum[a[i]]++;
mxnum=max(mxnum,sum[a[i]]);
}
printf("%d\n",ans);
}
}
E. Three Days Grace
题目大意
给定大小为 n ( 1 ≤ n ≤ 1 0 6 ) n(1 \leq n \leq 10^6) n(1≤n≤106) 的多重集合 A A A ,每个元素在 [ 1 , m ] ( 1 ≤ m ≤ 5 ⋅ 1 0 6 ) [1,m](1 \leq m \leq 5 \cdot 10^6) [1,m](1≤m≤5⋅106) 范围内。
可以进行任意次操作。每次操作选择 A A A 中一个元素 x x x ,将其拆分为 x = p ⋅ q x=p\cdot q x=p⋅q ,其中 p , q > 1 p,q >1 p,q>1 ,然后将 p , q p,q p,q 插入 A A A 中,并删除 x x x 。
求 max ( a i ) − min ( a i ) \max(a_i)-\min(a_i) max(ai)−min(ai) 的最小值。
题解
参考了huangxiaohua的AC代码和官方题解。
考虑枚举 L = m i n ( a i ) L=min(a_i) L=min(ai) ,求解最小的 R = m a x ( a i ) R=max(a_i) R=max(ai) 。
从大到小枚举 L L L ,当枚举到新的 L L L 时,会发现对于 L L L 的倍数 j j j ,可能有更好的拆分方法,从而得到更小的 R R R 。对于不是 L L L 的倍数的数,并不会受到影响。
设 d p i , j dp_{i,j} dpi,j 表示将 j j j 拆分为若干因子后,最小因子为 i i i 时所有因子的最大值的最小值,其中 $j\geq i $ 且 i ∣ j i|j i∣j 。
对于 k ≥ i k\geq i k≥i , 若 d p k , j i ≥ i dp_{k,\frac{j}{i}}\geq i dpk,ij≥i ,则可以将 j j j 拆出一个 i i i ,变成子状态 d p k , j i dp_{k,\frac{j}{i}} dpk,ij ,即
d p i , j = min ( d p i , j , d p k , j i ) dp_{i,j}=\min(dp_{i,j},dp_{k,\frac{j}{i}}) dpi,j=min(dpi,j,dpk,ij)
实现时, i i i 维度可以通过从大到小枚举被压缩掉,复杂度 O ( m log m ) O(m\log m) O(mlogm)。
当枚举到新的 L L L 时,对于初始就有的元素 j j j ,其中 L ∣ j L|j L∣j ,先删去所有旧的 d p k , j dp_{k,j} dpk,j ,其中 k > L k>L k>L ,再更新并添加上新的 d p L , j dp_{L,j} dpL,j 。
参考代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXM=5000500;
int has[MAXM],num[MAXM],dp[MAXM];
int main()
{
int T,n,m,l,r,ans,i,j,x;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
memset(has,0,sizeof(int)*(m+5));
memset(num,0,sizeof(int)*(m+5));
for(i=1;i<=m;i++)
dp[i]=i;
l=MAXM;r=-1;
while(n--)
{
scanf("%d",&x);
has[x]=num[x]=1;
l=min(l,x);
r=max(r,x);
}
ans=r-l;
for(i=sqrt(m)+1;i>=1;i--)
{
for(j=i;j<=m;j+=i)
{
if(has[j])
num[dp[j]]--;
if(dp[j/i]>=i)
dp[j]=min(dp[j/i],dp[j]);
if(has[j])
num[dp[j]]++;
}
while(num[r]==0)
r--;
ans=min(ans,r-min(i,l));
}
printf("%d\n",ans);
}
}