Codeforces Round #732 div.2 A-E题解

div.2 视频讲解:BV1cU4y1G7CQ
div.1 视频讲解:TBD

div.2-A. AquaMoon and Two Arrays

题目大意

给定两个长度为 n ( 1 ≤ n ≤ 100 ) n(1 \leq n \leq 100) n(1n100) 的数组 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(0ai,bi100,ai100,bi100)
你可以对数组 a a a 修改不超过 100 100 100 次,每次可以选择一对 ( i , j ) ( 1 ≤ i , j ≤ n ) (i,j)(1 \leq i,j \leq n) (i,j)(1i,jn) ,使得 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(1n105) 个长度为 m ( 1 ≤ m ≤ 1 0 5 ) m(1 \leq m \leq 10^5) m(1m105) 的字符串。 ( n ⋅ m ≤ 1 0 5 ) (n \cdot m \leq 10^5) (nm105)
将其中的 n − 1 n-1 n1 个字符串组成 n − 1 2 \frac{n-1}{2} 2n1 对。每对中,选择某些位置,调换这两个字符串的对应位置字符。
现在直到初始的 n n n 个字符串,和配对修改后的 n − 1 n-1 n1 个字符串,求未配对的字符串。

题解

可以想到,对于任意位置 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(1n105) 个人排列在一条直线上,第 i i i 个人穿着 a i ( 1 ≤ a i ≤ 1 0 5 ) a_i(1 \leq a_i \leq 10^5) ai(1ai105) 号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(1n105) 的棋盘上完跳棋。初始某些位置上有棋子,其他位置上没棋子。
每次移动棋子时,可以选择位置 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 i1 上有棋子,位置 i − 2 i-2 i2 上没棋子,则可以移动到位置 i − 2 i-2 i2 上。

求从初始棋盘布局开始,可以通过操作得到多少种不同的棋盘布局。

题解

根据跳棋的移动规则,假设有三个相邻位置,前两个有棋子,最后一个没有棋子,即 ( 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(5n500) 个大小为 n n n 的数组恰好构成拉丁矩阵 a a a。现在对其进行以下修改:

  1. 再添加 n n n 个大小为 n n n 的数组,使得对于任意 1 ≤ i ≤ n 1 \leq i \leq n 1in ,至少存在一个 1 ≤ k ≤ n 1 \leq k \leq n 1kn ,满足第 i i i 个数组与第 n + i n+i n+i 个数组的第 k k k 个元素相同。且保证这 2 n 2n 2n 个数组,他们互不相同。注意这些额外数组不一定需要构成拉丁矩阵。
  2. 重新对这些数组按任意顺序排列。

现在给定修改后的 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(5m1000) 个人排成一排,编号从 0 0 0 m − 1 m-1 m1 。第 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+tvi 位置。
记录下 k ( 7 ≤ k ≤ 1000 ) k(7 \leq k \leq 1000) k(7k1000) 个连续时刻这 m m m 个人的位置,时刻从 0 0 0 m − 1 m-1 m1 。在每一时刻,这 m m m 个人的坐标以任意顺序给出。
现在修改时刻 y ( 0 < y < k − 1 ) y(0 < y < k-1) y(0<y<k1) 中的某一坐标值,将其修改为另一个不同的整数。
给出修改后的 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+tvi ,可以发现 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=0mxi2+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=sum2tsum2t1=i=0m2xivi+(2t1)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=sum2tsum2t1=i=0m2vi2

因此 ∑ 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}) wp2p2=sum2ysum20(i=0m2txivi+t2vi2)

由于 w p − p wp-p wpp 可以在校对 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) wp2p2=(wp+p)(wpp)

即可求得 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

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值