div.2 视频讲解:BV1cU4y1G7CQ
div.1 视频讲解:TBD
div.2-A. AquaMoon and Two Arrays
题目大意
给定两个长度为
n
(
1
≤
n
≤
100
)
n(1 \leq n \leq 100)
n(1≤n≤100) 的数组
a
,
b
(
0
≤
a
i
,
b
i
≤
100
,
∑
a
i
≤
100
,
∑
b
i
≤
100
)
a,b(0 \leq a_i,b_i \leq 100,\sum{a_i} \leq 100 ,\sum{b_i} \leq 100)
a,b(0≤ai,bi≤100,∑ai≤100,∑bi≤100) 。
你可以对数组
a
a
a 修改不超过
100
100
100 次,每次可以选择一对
(
i
,
j
)
(
1
≤
i
,
j
≤
n
)
(i,j)(1 \leq i,j \leq n)
(i,j)(1≤i,j≤n) ,使得
a
i
a_i
ai 增加
1
1
1 ,
a
j
a_j
aj 减少
1
1
1 。
求能否在
100
100
100 次操作内,将数组
a
a
a 修改为数组
b
b
b 。
题解
若
∑
a
i
≠
∑
b
i
\sum{a_i} \neq \sum{b_i}
∑ai=∑bi ,则不行,反之可以。
求操作序列时,可以将增加和减少单独考虑,减少代码复杂度。
参考代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAXN=110;
int a[MAXN],b[MAXN],opi[MAXN],opj[MAXN];
int main()
{
int T,n,i,cnti,cntj,asum,bsum;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
asum=bsum=0;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
asum+=a[i];
}
for(i=1;i<=n;i++)
{
scanf("%d",&b[i]);
bsum+=b[i];
}
if(asum!=bsum)
{
printf("-1\n");
continue;
}
cnti=cntj=0;
for(i=1;i<=n;i++)
{
while(a[i]>b[i])
{
opi[cnti++]=i;
a[i]--;
}
while(a[i]<b[i])
{
opj[cntj++]=i;
a[i]++;
}
}
printf("%d\n",cnti);
for(i=0;i<cnti;i++)
{
printf("%d %d\n",opi[i],opj[i]);
}
}
}
div.2-B. AquaMoon and Stolen String
题目大意
有奇数
n
(
1
≤
n
≤
1
0
5
)
n(1 \leq n \leq 10^5)
n(1≤n≤105) 个长度为
m
(
1
≤
m
≤
1
0
5
)
m(1 \leq m \leq 10^5)
m(1≤m≤105) 的字符串。
(
n
⋅
m
≤
1
0
5
)
(n \cdot m \leq 10^5)
(n⋅m≤105)
将其中的
n
−
1
n-1
n−1 个字符串组成
n
−
1
2
\frac{n-1}{2}
2n−1 对。每对中,选择某些位置,调换这两个字符串的对应位置字符。
现在直到初始的
n
n
n 个字符串,和配对修改后的
n
−
1
n-1
n−1 个字符串,求未配对的字符串。
题解
可以想到,对于任意位置
i
i
i ,配对前
n
n
n 个字符串中的第
i
i
i 个位置的字符构成的多重集合,与配对修改后
n
n
n 个字符串中的第
i
i
i 个位置的字符构成的多重集合是一样的。以此统计配对前后每个位置上缺少哪个字符即可。
具体实现时,可以用求和或者异或简化代码。
参考代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAXN=100100;
char s[MAXN];
int main()
{
int T,n,m,i,j;
char c;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(i=0;i<m;i++)
s[i]=0;
for(i=1;i<2*n;i++)
{
for(j=0;j<m;j++)
{
scanf(" %c",&c);
s[j]^=c;
}
}
for(i=0;i<m;i++)
printf("%c",s[i]);
puts("");
fflush(stdout);
}
}
div.2-C/div.1-A. AquaMoon and Strange Sort
题目大意
有
n
(
1
≤
n
≤
1
0
5
)
n(1 \leq n \leq 10^5)
n(1≤n≤105) 个人排列在一条直线上,第
i
i
i 个人穿着
a
i
(
1
≤
a
i
≤
1
0
5
)
a_i(1 \leq a_i \leq 10^5)
ai(1≤ai≤105) 号T恤,并且初始时,每个人都朝右。
你可以任意次数改变他们的位置。每次选择两个相邻的人,交换他们的位置,并让他们的朝向相反的方向。
求能否让所有人的T恤编号从左到右为不下降序列,且每个人都朝右。
题解
如果希望使得修改后每个人朝向不变,则每个人的移动步数,均为
2
2
2 的倍数。以此若一个人原先在奇数位置,修改后也在奇数位置。偶数同理。
将位置按奇偶分为两个多重集合,则排序前后,两个集合内的元素应该保持不变。若不能,则无解。
实现时,分别构建两个计数数组对奇偶位置的T恤计数即可。
参考代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAXN=100100;
int a[MAXN],num[2][MAXN];
int main()
{
int T,n,i,flag;
scanf("%d",&T);
while(T--)
{
memset(num,0,sizeof(num));
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
num[i&1][a[i]]++;
}
sort(a+1,a+n+1);
flag=1;
for(i=1;i<=n;i++)
{
if(!num[i&1][a[i]])
{
flag=0;
break;
}
num[i&1][a[i]]--;
}
if(flag)
printf("YES\n");
else
printf("NO\n");
}
}
div.2-D/div.1-B. AquaMoon and Chess
题目大意
在
1
×
n
(
1
≤
n
≤
1
0
5
)
1 \times n(1 \leq n \leq 10^5)
1×n(1≤n≤105) 的棋盘上完跳棋。初始某些位置上有棋子,其他位置上没棋子。
每次移动棋子时,可以选择位置
i
i
i 上的棋子,然后进行以下操作之一:
- 若位置 i + 1 i+1 i+1 上有棋子,位置 i + 2 i+2 i+2 上没棋子,则可以移动到位置 i + 2 i+2 i+2 上。
- 若位置 i − 1 i-1 i−1 上有棋子,位置 i − 2 i-2 i−2 上没棋子,则可以移动到位置 i − 2 i-2 i−2 上。
求从初始棋盘布局开始,可以通过操作得到多少种不同的棋盘布局。
题解
根据跳棋的移动规则,假设有三个相邻位置,前两个有棋子,最后一个没有棋子,即
(
1
,
1
,
0
)
(1,1,0)
(1,1,0) 的状态,那么最左边的棋子可以跳到最右边,变为
(
0
,
1
,
1
)
(0,1,1)
(0,1,1) 状态。
因此,可以将两个连续的棋子捆绑在一起,用
(
2
)
(2)
(2) 表示。这样原先的变化过程,就是
(
2
,
0
)
(2,0)
(2,0) 变化为
(
0
,
2
)
(0,2)
(0,2) ,即交换。
对于初始状态中连续的偶数个数棋子,例如 ( 0 , 1 , 1 , 1 , 1 , 0 ) (0,1,1,1,1,0) (0,1,1,1,1,0) ,可以将其表示为 ( 0 , 2 , 2 , 0 ) (0,2,2,0) (0,2,2,0) 。对其中的 2 2 2 与 0 0 0 任意排序,可以得到以下几种情况:
- ( 0 , 0 , 2 , 2 ) ⟺ ( 0 , 0 , 1 , 1 , 1 , 1 ) (0,0,2,2) \iff (0,0,1,1,1,1) (0,0,2,2)⟺(0,0,1,1,1,1)
- ( 0 , 2 , 0 , 2 ) ⟺ ( 0 , 1 , 1 , 0 , 1 , 1 ) (0,2,0,2) \iff (0,1,1,0,1,1) (0,2,0,2)⟺(0,1,1,0,1,1)
- ( 0 , 2 , 2 , 0 ) ⟺ ( 0 , 1 , 1 , 1 , 1 , 0 ) (0,2,2,0) \iff (0,1,1,1,1,0) (0,2,2,0)⟺(0,1,1,1,1,0)
- ( 2 , 0 , 0 , 2 ) ⟺ ( 1 , 1 , 0 , 0 , 1 , 1 ) (2,0,0,2) \iff (1,1,0,0,1,1) (2,0,0,2)⟺(1,1,0,0,1,1)
- ( 2 , 0 , 2 , 0 ) ⟺ ( 1 , 1 , 0 , 1 , 1 , 0 ) (2,0,2,0) \iff (1,1,0,1,1,0) (2,0,2,0)⟺(1,1,0,1,1,0)
- ( 2 , 2 , 0 , 0 ) ⟺ ( 1 , 1 , 1 , 1 , 0 , 0 ) (2,2,0,0) \iff (1,1,1,1,0,0) (2,2,0,0)⟺(1,1,1,1,0,0)
以上情况均可从初始状态转移得到。
对于初始状态中连续的奇数个数棋子,例如 ( 0 , 1 , 1 , 1 , 0 ) (0,1,1,1,0) (0,1,1,1,0) ,可以将其表示为 ( 0 , 1 , 2 , 0 ) (0,1,2,0) (0,1,2,0) 或 ( 0 , 2 , 1 , 0 ) (0,2,1,0) (0,2,1,0) 。在其后续变化中,有以下几种情况:
- ( 0 , 1 , 0 , 1 , 1 ) ⟺ ( 0 , 1 , 0 , 2 ) (0,1,0,1,1) \iff (0,1,0,2) (0,1,0,1,1)⟺(0,1,0,2)
- ( 1 , 1 , 0 , 1 , 0 ) ⟺ ( 2 , 0 , 1 , 0 ) (1,1,0,1,0) \iff (2,0,1,0) (1,1,0,1,0)⟺(2,0,1,0)
- ( 0 , 1 , 1 , 1 , 0 ) ⟺ ( 0 , 1 , 2 , 0 ) / ( 0 , 2 , 1 , 0 ) (0,1,1,1,0) \iff (0,1,2,0)/(0,2,1,0) (0,1,1,1,0)⟺(0,1,2,0)/(0,2,1,0)
会发现,其中单独的 1 1 1 是无法改变的,只能修改其中 0 0 0 和 2 2 2 的顺序。
设有 s u m 2 sum2 sum2 对相邻的棋子,有 s u m 0 sum0 sum0 个空白,则答案为 2 2 2 和 0 0 0 的所有排序数量,即 C s u m 0 + s u m 2 s u m 2 C_{sum0+sum2}^{sum2} Csum0+sum2sum2 。
参考代码
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const ll mod=998244353;
const int MAXN=100100;
char s[MAXN];
ll f[MAXN],invf[MAXN];
ll powmod(ll x,ll p)
{
ll ret=1;
while(p)
{
if(p&1)
ret=ret*x%mod;
x=x*x%mod;
p>>=1;
}
return ret;
}
int main()
{
ll ans;
int T,n,i,sum0,sum1,sum2;
f[0]=invf[0]=1;
for(i=1;i<MAXN;i++)
f[i]=f[i-1]*i%mod;
invf[MAXN-1]=powmod(f[MAXN-1],mod-2);
for(i=MAXN-2;i>=1;i--)
invf[i]=invf[i+1]*(i+1)%mod;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
scanf("%s",&s);
sum0=sum1=sum2=0;
for(i=0;i<n;i++)
{
if(s[i]=='0')
sum0++;
else if(i+1<n&&s[i+1]=='1')
{
sum2++;
i++;
}
else
sum1++;
}
ans=f[sum2+sum0]*invf[sum2]%mod*invf[sum0]%mod;
printf("%lld\n",ans);
}
}
div.2-E/div.1-c. AquaMoon and Permutations
题目大意
拉丁矩阵指一种 n × n n \times n n×n 的方阵,每一行是 1 1 1 到 n n n 的排列,每一列也是 1 1 1 到 n n n 的排列。
有 n ( 5 ≤ n ≤ 500 ) n(5 \leq n \leq 500) n(5≤n≤500) 个大小为 n n n 的数组恰好构成拉丁矩阵 a a a。现在对其进行以下修改:
- 再添加 n n n 个大小为 n n n 的数组,使得对于任意 1 ≤ i ≤ n 1 \leq i \leq n 1≤i≤n ,至少存在一个 1 ≤ k ≤ n 1 \leq k \leq n 1≤k≤n ,满足第 i i i 个数组与第 n + i n+i n+i 个数组的第 k k k 个元素相同。且保证这 2 n 2n 2n 个数组,他们互不相同。注意这些额外数组不一定需要构成拉丁矩阵。
- 重新对这些数组按任意顺序排列。
现在给定修改后的 2 n 2n 2n 个数组,求其子集中,有多少个可以构成拉丁矩阵。并输出任意一种拉底矩阵。
题解
拉丁矩阵中每一列都是
1
1
1 到
n
n
n 的排列。
因此在未被的数组中,若有一个数组
a
i
a_{i}
ai 其中的某个数
a
i
,
j
a_{i,j}
ai,j 在其所在列
j
j
j 上只出现一次,那么它必定属于
n
n
n 个原始数组之一。选择它并删除与其至少有一个位置上元素相同的其他矛盾数组。
若在未被选择的数组中,如果不存在这样的数组
a
i
a_i
ai ,根据鸽巢原理,所有未选择数组上的元素在其所在列恰好出现两次。因此选择任意一种,并删除其他矛盾数组即可。此时方案数量要
×
2
\times 2
×2 。
重复上述操作,直到选择了
n
n
n 个数组。
(尚不明确,待详细证明,TBD)
参考代码
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAXN=510;
const int mod=998244353;
int a[MAXN<<1][MAXN],num[MAXN][MAXN],used[MAXN],res[MAXN];
int main()
{
int T,n,i,j,r,ans,p,flag;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(i=1;i<=2*n;i++)
{
used[i]=0;
for(j=1;j<=n;j++)
{
scanf("%d",&a[i][j]);
num[j][a[i][j]]++;
}
}
ans=1;
for(r=1;r<=n;r++)
{
p=0;
for(i=1;i<=2*n;i++)
{
if(used[i])
continue;
for(j=1;j<=n&&!p;j++)
{
if(num[j][a[i][j]]==1)
p=i;
}
}
if(!p)
{
ans=2*ans%mod;
for(p=1;used[p];p++);
}
used[p]=1;
for(j=1;j<=n;j++)
num[j][a[p][j]]--;
res[r]=p;
for(i=1;i<=2*n;i++)
{
if(used[i])
continue;
flag=0;
for(j=1;j<=n;j++)
{
if(a[i][j]==a[p][j])
flag=1;
}
if(flag)
{
used[i]=1;
for(j=1;j<=n;j++)
num[j][a[i][j]]--;
}
}
}
printf("%d\n",ans);
for(i=1;i<=n;i++)
printf("%d ",res[i]);
puts("");
}
}
div.2-F/div.1-D. AquaMoon and Wrong Coordinate
题目大意
有
m
(
5
≤
m
≤
1000
)
m(5 \leq m \leq 1000)
m(5≤m≤1000) 个人排成一排,编号从
0
0
0 到
m
−
1
m-1
m−1 。第
i
i
i 个人初始在
x
i
x_i
xi 位置,以
v
i
v_i
vi 的速度面朝右(正方向)前进,即在
t
t
t 时刻其在
x
i
+
t
⋅
v
i
x_i+t \cdot v_i
xi+t⋅vi 位置。
记录下
k
(
7
≤
k
≤
1000
)
k(7 \leq k \leq 1000)
k(7≤k≤1000) 个连续时刻这
m
m
m 个人的位置,时刻从
0
0
0 到
m
−
1
m-1
m−1 。在每一时刻,这
m
m
m 个人的坐标以任意顺序给出。
现在修改时刻
y
(
0
<
y
<
k
−
1
)
y(0 < y < k-1)
y(0<y<k−1) 中的某一坐标值,将其修改为另一个不同的整数。
给出修改后的
k
k
k 个连续时刻
m
m
m 个人的坐标,求
y
y
y 和修改前的坐标。
题解
时刻 t t t 所有人的坐标之和为 s u m t = ∑ i = 0 m x i + t ⋅ v i sum_t=\sum_{i=0}^{m}{x_i+t \cdot v_i} sumt=∑i=0mxi+t⋅vi ,可以发现 s u m t sum_t sumt 是等差数列,因此找出不符合条件的时刻,就是被修改的时刻 y y y 。
在数据正确的情况下,时刻
t
t
t 所有人的坐标平方之和为
s
u
m
2
t
=
∑
i
=
0
m
x
i
2
+
2
t
x
i
v
i
+
t
2
v
i
2
sum2_t=\sum_{i=0}^{m}{x_i^2+2tx_iv_i+t^2v_i^2}
sum2t=i=0∑mxi2+2txivi+t2vi2
对其差分,得到
s
u
m
2
t
′
=
s
u
m
2
t
−
s
u
m
2
t
−
1
=
∑
i
=
0
m
2
x
i
v
i
+
(
2
t
−
1
)
v
i
2
sum2'_t=sum2_t-sum2_{t-1}=\sum_{i=0}^m{2x_iv_i+(2t-1)v_i^2}
sum2t′=sum2t−sum2t−1=i=0∑m2xivi+(2t−1)vi2
再对其差分,得到
s
u
m
2
t
′
′
=
s
u
m
2
t
′
−
s
u
m
2
t
−
1
′
=
∑
i
=
0
m
2
v
i
2
sum2''_t=sum2'_t-sum2'_{t-1}=\sum_{i=0}^m{2v_i^2}
sum2t′′=sum2t′−sum2t−1′=i=0∑m2vi2
因此
∑
i
=
0
m
v
i
2
=
s
u
m
2
t
′
′
2
\sum_{i=0}^m{v_i^2}=\frac{sum2''_t}{2}
∑i=0mvi2=2sum2t′′ 。
对于
∑
i
=
0
m
v
i
x
i
\sum_{i=0}^m{v_ix_i}
∑i=0mvixi ,可以将其带入
s
u
m
2
t
′
sum2'_t
sum2t′ 求得。
设修改前的数据为
p
p
p ,修改后的数据为
w
p
wp
wp ,那么
w
p
2
−
p
2
=
s
u
m
2
y
−
s
u
m
2
0
−
(
∑
i
=
0
m
2
t
x
i
v
i
+
t
2
v
i
2
)
wp^2-p^2=sum2_y-sum2_0-(\sum_{i=0}^m{2tx_iv_i+t^2v_i^2})
wp2−p2=sum2y−sum20−(i=0∑m2txivi+t2vi2)
由于
w
p
−
p
wp-p
wp−p 可以在校对
s
u
m
t
sum_t
sumt 时求出,带入
w
p
2
−
p
2
=
(
w
p
+
p
)
(
w
p
−
p
)
wp^2-p^2=(wp+p)(wp-p)
wp2−p2=(wp+p)(wp−p)
即可求得 p p p 。
参考代码
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAXN=1010;
ll a[MAXN][MAXN],sum[MAXN],sum2[MAXN];
int main()
{
int m,k,i,j;
ll sumd,y,y1,p,d,A,B,v2,vx2;
scanf("%d%d",&m,&k);
for(i=0;i<k;i++)
{
for(j=0;j<m;j++)
{
scanf("%lld",&a[i][j]);
sum[i]+=a[i][j];
sum2[i]+=a[i][j]*a[i][j];
}
}
sumd=(sum[k-1]-sum[0])/(k-1);
for(i=1;i<k;i++)
{
if(sum[i]-sum[0]!=sumd*i)
{
y=i;
break;
}
}
y1=(y==1?2:1);
d=sum[y]-sum[0]-sumd*y;
A=(sum2[k-1]-sum2[0])/(k-1);
B=(sum2[y1]-sum2[0])/y1;
v2=(A-B)/(k-1-y1);
vx2=B-v2*y1;
p=((sum2[y]-sum2[0]-v2*y*y-vx2*y)/d-d)/2;
printf("%lld %lld\n",y,p);
fflush(stdout);
}
TBD