A.Array Divisibility(构造)
题意:
如果一个整数数组 a 1 , a 2 , ⋯ , a n a_1,a_2,\cdots,a_n a1,a2,⋯,an满足以下条件,那么这个数组就是美丽的整数 k k k:
- 所有 j j j上的 a j a_{j} aj之和,其中 j j j是 k k k的倍数, 1 ≤ j ≤ n 1\le j\le n 1≤j≤n是 k k k的倍数。本身是 k k k的倍数。
- 更正式地说,如果 ∑ k ∣ j a j \sum_{k|j}a_{j} ∑k∣jaj能被所有 1 ≤ j ≤ n 1\le j\le n 1≤j≤n的 k k k整除,那么数组 a a a在 k k k的限制下是美丽的。这里,符号 k ∣ j {k|j} k∣j表示 k k k除以 j j j,即 j j j是 k k k的倍数。
给定 n n n,求一个正非零整数数组,其中每个元素都小于或等于 1 0 5 10^5 105,且所有 1 ≤ k ≤ n 1\le k\le n 1≤k≤n都是美丽的。
可以证明答案总是存在的。
分析:
简单构造题,很容易发现输出 1 − n 1-n 1−n满足题意。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
int n;
int main(){
int t;
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;++i)
cout<<i<<" ";
cout<<endl;
}
return 0;
}
B.Corner Twist(数学)
题意:
给你两个网格,分别是 a a a和 b b b,行数为 n n n,列数为 m m m。网格中的所有数值都是 0 0 0、 1 1 1或 2 2 2。
您可以多次对 a a a执行以下操作:
- 选择网格中任意一个长宽 ≥ 2 \ge 2 ≥2的子矩形。您可以选择整个网格作为子矩形。
- 子矩形有四个角。取所选子矩形中任意一对斜对角,并将它们的值加上 1 1 1,对 3 3 3取模。
- 对于未选中的一对角,在它们的值上加上 2 2 2然后对 3 3 3取模。
需要注意的是,此操作只改变被选中的子矩形的角的值。
是否可以通过任意次数(可能为零)的上述操作将网格 a a a转换为网格 b b b?
分析:
观察样例发现,每次操作后,一行中的数对 3 3 3取模的值不变,一列中的数对 3 3 3取模的值不变。因此,我们只需比较两个矩阵每行的数的和对 3 3 3取模的值与每列的数的和对 3 3 3取模的值,如果都相同则有解;否则无解。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int MAXN=500;
int a[MAXN+10][MAXN+10];
int b[MAXN+10][MAXN+10],m,n;
int sumn[14][MAXN+10];
char s[MAXN+10];
void solve(){
int i,j;
cin>>n>>m;
memset(sumn,0,sizeof(sumn));
for(i=1;i<=n;++i){
cin>>s;
for(j=1;j<=m;++j){
a[i][j]=s[j-1]-'0';
sumn[1][i]=(sumn[1][i]+a[i][j])%3;
sumn[2][j]=(sumn[2][j]+a[i][j])%3;
}
}
for(i=1;i<=n;++i){
cin>>s;
for(j=1;j<=m;++j){
b[i][j]=s[j-1]-'0';
sumn[3][i]=(sumn[3][i]+b[i][j])%3;
sumn[4][j]=(sumn[4][j]+b[i][j])%3;
}
}
for(i=1;i<=n;++i)
if(sumn[1][i]!=sumn[3][i])
break;
for(j=1;j<=m;++j)
if(sumn[2][j]!=sumn[4][j])
break;
if(i==n+1&&j==m+1)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}
C.Have Your Cake and Eat It Too(枚举)
题意:
爱丽丝、鲍勃和查理想分享一个切成 n n n块的长方形蛋糕。每个人认为每块蛋糕的价值都不同。爱丽丝认为第 i i i块的价值是 a i a_i ai,鲍勃认为是 b i b_i bi,而查理认为是 c i c_i ci。
所有 a i a_i ai、所有 b i b_i bi和所有 c i c_i ci的总和是相同的,等于 t o t tot tot。
考虑到每个人的每块蛋糕的值,需要给每个人一块连续的蛋糕。换句话说,对于爱丽丝、鲍勃和查理来说,这些子数组左端和右端的索引(给每个人的蛋糕片)可以分别表示为 ( l a , r a ) (l_a,r_a) (la,ra)、 ( l b , r b ) (l_b,r_b) (lb,rb)和 ( l c , r c ) (l_c,r_c) (lc,rc)。分割需要满足以下约束条件:
- 没有一块蛋糕被分配给多人,即 [ l a , … , r a ] [l_a,\ldots,r_a] [la,…,ra]、 [ l b , … , r b ] [l_b,\ldots,r_b] [lb,…,rb]和 [ l c , … , r c ] [l_c,\ldots,r_c] [lc,…,rc]中没有两个子数组相交。
- ∑ i = l a r a a i , ∑ i = l b r b b i , ∑ i = l c r c c i ≥ ⌈ t o t 3 ⌉ \sum_{i=l_a}^{r_a}a_i,\sum_{i=l_b}^{r_b}b_i,\sum_{i=l_c}^{r_c}c_i\geq\lceil\frac{tot}{3}\rceil ∑i=laraai,∑i=lbrbbi,∑i=lcrcci≥⌈3tot⌉。
这里,符号 ⌈ a b ⌉ \lceil \frac{a}{b} \rceil ⌈ba⌉ 代表上限除法。
分析:
把 n n n块蛋糕分成 3 3 3块连续的蛋糕,共有6种顺序。枚举这 6 6 6种顺序,对于每种顺序,先遍历第一个人对应的数组,将值累加,总和达到 ⌈ t o t 3 ⌉ \lceil \frac{tot}{3} \rceil ⌈3tot⌉时将总和清零,把左右端点存下来,并转到下一个人对应的数组。当循环结束时,判断第 3 3 3个人的左右端点是否已经存储,即可得到方案是否合法。如果合法,停止;否则,遍历下一种顺序。最后输出答案。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int b[16][13]={{},{0,1,2,3},{0,1,3,2},{0,2,1,3},{0,2,3,1},{0,3,1,2},{0,3,2,1}};
const int maxn=2e5;
int a[13][maxn+10],lt[13],rt[13];
int f,n;
LL sumn,tot;
void solve(){
int i,j;
scanf("%d",&n);
for(i=1;i<=3;++i)
for(j=1;j<=n;++j)
scanf("%d",&a[i][j]);
tot=0;
for(i=1;i<=n;++i)
tot+=a[1][i];
tot=(tot+2)/3;
for(i=1;i<=6;++i){
f=1;
sumn=0;
for(j=1;j<=n;++j){
sumn+=a[b[i][f]][j];
if(sumn>=tot){
sumn=0;
lt[b[i][f]]=rt[b[i][f-1]]+1;
rt[b[i][f]]=j;
++f;
}
}
if(f>=4){
rt[b[i][3]]=n;
break;
}
}
if(i<=6){
for(i=1;i<=3;++i)
printf("%d %d ",lt[i],rt[i]);
printf("\n");
}
else
printf("-1\n");
}
int main(){
int t;
scanf("%d",&t);
while(t--){
solve();
}
return 0;
}
D.Swap Dilemma(树状数组)
题意:
给定长度为 n n n的两个不同正整数数组 a a a和 b b b,我们希望使这两个数组相同。长度为 k k k的两个数组 x x x和 y y y在所有的 1 ≤ i ≤ k 1\le i\le k 1≤i≤k, x i = y i x_i=y_i xi=yi中都相同。
现在只需走一步,就可以在 a a a( l ≤ r l\le r l≤r)中选择索引 l l l和 r r r,并交换 a l a_l al和 a r a_r ar,然后在 b b b中选择索引 p p p和 q q q( p ≤ q p\le q p≤q),这样就可以选择 r − l = q − p r-l=q-p r−l=q−p,并交换 b p b_p bp和 b q b_q bq。
有可能使两个数组相同吗?
分析:
考虑如果 a a a和 b b b能通过若干次这样的操作后相同,那么同样可以若干次操作后令 a , b a,b a,b序列变得有序。
考虑到交换不相邻的两个数的操作,可以由若干次交换相邻的数的操作完成。我们不妨操作时只交换相邻的数,这样要求 r − l = q − p r−l=q−p r−l=q−p的限制就被我们弄没了。发现如果排序 a a a和 b b b所需交换数相同,那么我们就能将对 a a a的操作与对 b b b的操作两两匹配构造方案,而且由于我们能反复交换两个数,因此只要交换次数的奇偶性相同即可。
这样我们就把问题转化为了排序一个序列需要交换相邻的数多少次,这个东西可以随便做,随便找一种排序方式算一下就行。我用的是用树状数组维护插入排序的过程,计算交换数。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
#define MAXN 200005
using namespace std;
LL n;
LL a[MAXN],b[MAXN],cnt[MAXN];
LL A[MAXN],pos[MAXN];
LL bit[MAXN];
void add(LL x,LL z){while(x<=n){bit[x]+=z;x+=x&-x;}}
LL sum(LL x){LL res=0;while(x){res+=bit[x];x-=x&-x;}return res;}
LL get(){
LL res=0;
for(LL i=1;n>=i;i++)
bit[i]=0;
for(LL i=1;n>=i;i++){
pos[A[i]]=i;
add(i,1);
}
for(LL i=1;n>=i;i++){
res+=sum(pos[i])-i;
add(1,1);add(pos[i],-1);
}
return res;
}
void solve(){
cin>>n;
for(LL i=1;n>=i;i++)cin>>a[i];
for(LL i=1;n>=i;i++)cin>>b[i];
LL flg=0;
for(LL i=1;n>=i;i++)cnt[a[i]]=0;
for(LL i=1;n>=i;i++){
cnt[a[i]]++;
if(cnt[a[i]]==2)flg=1;
}
for(LL i=1;n>=i;i++)cnt[b[i]]--;
for(LL i=1;n>=i;i++)
if(cnt[a[i]]){
cout<<"NO"<<endl;
return ;
}
if(flg){
cout<<"YES"<<endl;
return;
}
vector<LL>v;
map<LL,LL>ma;
for(LL i=1;n>=i;i++)
v.push_back(a[i]);
sort(v.begin(),v.end());
for(LL i=0;n>i;i++)
ma[v[i]]=i+1;
for(LL i=1;n>=i;i++)
a[i]=ma[a[i]];
for(LL i=1;n>=i;i++)
b[i]=ma[b[i]];
for(LL i=1;n>=i;i++)
A[i]=a[i];
LL t1=get();
for(LL i=1;n>=i;i++)
A[i]=b[i];
LL t2=get();
if(t1%2==t2%2){
cout<<"YES"<<endl;
}
else
cout<<"NO"<<endl;
}
int main(){
LL T;
cin>>T;
while(T--)
solve();
return 0;
}
E.I Love Balls(数学)
题意:
爱丽丝和鲍勃正在玩一个游戏。游戏中有 n n n个球,其中 k k k个是特殊球。每个球都有一个与之相关的值。
玩家轮流进行游戏。在每个回合中,玩家随机挑选一个球,并将该球的价值加到自己的分数中,游戏开始时的分数为 0 0 0。被选中的球将从游戏中移除。如果该球是特殊球,如果至少还有一个球,则由同一名玩家进行下一轮游戏。如果选中的球不是特殊球,则由下一位玩家进行下一轮游戏。
他们一直玩到游戏中没有剩余的球为止。爱丽丝先玩。
求游戏结束时双方的期望得分,对 1 0 9 + 7 10^9+7 109+7取模。
形式上,让 M = 1 0 9 + 7 M=10^9+7 M=109+7。可以证明答案可以用不可约分数 p q \frac{p}{q} qp表示,其中 p p p和 q q q是整数,而 q ≢ 0 ( m o d M ) q\not\equiv 0\pmod{M} q≡0(modM)是不可约分数。输出等于 p ⋅ q − 1 m o d M p\cdot q^{-1}\bmod M p⋅q−1modM的整数。换句话说,输出这样一个整数 x x x,即 0 ≤ x < M 0\le x\lt M 0≤x<M和 x ⋅ q ≡ p ( m o d M ) x\cdot q\equiv p\pmod{M} x⋅q≡p(modM)。
分析:
直觉上考虑,每个球归属两人的概率均为 1 2 \frac{1}{2} 21,由此对每个球归属两人的概率进行考虑。
若一个球归属爱丽丝,则在此之前摸到了偶数个非特殊球;反之则归属鲍勃。摸球被分成了 n − k + 1 n−k+1 n−k+1个段,除最后一段外,每段以一个非特殊球结尾,每个特殊球等概率地落在每段中,而每个非特殊球等概率地落在前 n − k n−k n−k段,落在奇数段的球归属爱丽丝,反之则归属鲍勃。
综上,特殊球归属爱丽丝的概率是 ⌈ n − k + 1 2 ⌉ n − k + 1 \frac{\lceil\frac{n−k+1}{2}\rceil}{ n−k+1} n−k+1⌈2n−k+1⌉,非特殊球归属爱丽丝的概率为 ⌈ n − k 2 ⌉ n − k \frac{\lceil\frac{n−k}{2}\rceil}{ n−k} n−k⌈2n−k⌉,由此即可计算期望。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int mod=1e9+7;
int cal(int a, int b) {
int res=1;
while(b){
if(b&1){
res=((LL)res*a)%mod;
}
a=((LL)a*a)%mod;
b>>=1;
}
return res;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;
cin>>T;
int n,k;
while(T--) {
int x,spe,sum,ansA,ansB;
cin>>n>>k;
spe = sum = 0;
for(int i=1;i<=n;++i) {
cin>>x;
if(i<=k){
spe=(spe+x)%mod;
}
else{
sum=(sum+x)%mod;
}
}
ansA = ((LL)((n - k + 2) / 2) * cal(n - k + 1, mod - 2) % mod * spe % mod+(LL)((n - k + 1) / 2) * cal(n - k, mod - 2) % mod * sum % mod) % mod;
ansB = ((LL)((n - k + 1) / 2) * cal(n - k + 1, mod - 2) % mod * spe % mod+(LL)((n - k) / 2) * cal(n - k, mod - 2) % mod * sum % mod) % mod;
cout<<ansA<<" "<<ansB<<endl;
}
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。