Codeforces Round #956 (Div. 2) A~E

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 1jn k k k的倍数。本身是 k k k的倍数。
  • 更正式地说,如果 ∑ k ∣ j a j \sum_{k|j}a_{j} kjaj能被所有 1 ≤ j ≤ n 1\le j\le n 1jn k k k整除,那么数组 a a a k k k的限制下是美丽的。这里,符号 k ∣ j {k|j} kj表示 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 1kn都是美丽的。

可以证明答案总是存在的。

分析:

简单构造题,很容易发现输出 1 − n 1-n 1n满足题意。

代码:

#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=lcrcci3tot

这里,符号 ⌈ 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 1ik, x i = y i x_i=y_i xi=yi中都相同。

现在只需走一步,就可以在 a a a l ≤ r l\le r lr)中选择索引 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 pq),这样就可以选择 r − l = q − p r-l=q-p rl=qp,并交换 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 rl=qp的限制就被我们弄没了。发现如果排序 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} q0(modM)是不可约分数。输出等于 p ⋅ q − 1   m o d   M p\cdot q^{-1}\bmod M pq1modM的整数。换句话说,输出这样一个整数 x x x,即 0 ≤ x < M 0\le x\lt M 0x<M x ⋅ q ≡ p ( m o d M ) x\cdot q\equiv p\pmod{M} xqp(modM)

分析:

直觉上考虑,每个球归属两人的概率均为 1 2 \frac{1}{2} 21,由此对每个球归属两人的概率进行考虑。

若一个球归属爱丽丝,则在此之前摸到了偶数个非特殊球;反之则归属鲍勃。摸球被分成了 n − k + 1 n−k+1 nk+1个段,除最后一段外,每段以一个非特殊球结尾,每个特殊球等概率地落在每段中,而每个非特殊球等概率地落在前 n − k n−k nk段,落在奇数段的球归属爱丽丝,反之则归属鲍勃。

综上,特殊球归属爱丽丝的概率是 ⌈ n − k + 1 2 ⌉ n − k + 1 \frac{\lceil\frac{n−k+1}{2}\rceil}{ n−k+1} nk+12nk+1,非特殊球归属爱丽丝的概率为 ⌈ n − k 2 ⌉ n − k \frac{\lceil\frac{n−k}{2}\rceil}{ n−k} nk2nk,由此即可计算期望。

代码:

#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,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值