Atcoder AGC043B(123 Triangle)题解

专门为这题写个题解。

Description

给定一个仅包含 1 , 2 , 3 1,2,3 1,2,3的序列,每次这个序列的长度减 1 1 1,第 i i i位上的数变为原来序列中第 i i i位减去第 i + 1 i+1 i+1位的数的绝对值

当这个序列的长度变成 1 1 1时,唯一那个数的值。

Solution

首先,我们可以发现几个性质:

①经历了第一次变换后, 3 3 3消失了, 0 0 0可能会出现。
这个很容易证: 1 , 2 , 3 1,2,3 1,2,3中任选两个可以相同的数相减的值的绝对值,一定不为 3 3 3

②答案为 0 0 0 1 1 1 2 2 2
证明方法与①同理。

③如果经历了一次变换后序列中存在 1 1 1,则答案不能为 2 2 2
假设第 i i i个位置为 1 1 1,在第 i i i个位置左边的数为 0 0 0 1 1 1 2 2 2。经历了一次变换后,位置为 i − 1 i-1 i1的这个数只能是 0 0 0 1 1 1,因为 ∣ 2 − 1 ∣ = ∣ 0 − 1 ∣ = 1 , ∣ 1 − 1 ∣ = 0 |2-1|=|0-1|=1, |1-1|=0 21=01=1,11=0。扩展到整个序列的情况, 2 2 2会因为 1 1 1的存在慢慢消失。

于是,我们先对这个序列做一次变换,并开始大力分讨。


假设当前序列中只有 0 0 0 1 1 1。可以发现,最终答案只能是 0 0 0 1 1 1

由于 a = 0 / 1 , b = 0 / 1 a=0/1, b=0/1 a=0/1,b=0/1时有 a + b = ∣ a − b ∣ a+b=|a-b| a+b=ab,所以,我们每次的变换相当于"第 i i i位上的数变为原来序列中第 i i i加上 i + 1 i+1 i+1位的数"。

我们用字母写下这个序列:

a b c d e

经历许多次变换后,序列分别长这样:

a b c d e
a+b b+c c+d d+e
a+2b+c b+2c+d c+2d+e
a+3b+3c+d b+3c+3d+e
a+4b+6c+4d+e

观察一下第一列的数的各项系数,可以发现:这与杨辉三角形有关!

即,假设 01 01 01序列为 a 1 , a 2 , a 3 … … a n a_1,a_2,a_3……a_n a1,a2,a3an,那么答案就是 ( ∑ i = 1 n C n − 1 i − 1 a i )   m o d   2 (\sum_{i=1}^n C_{n-1}^{i-1} a_i)\ mod\ 2 (i=1nCn1i1ai) mod 2

假设 k ! k! k!中质因数 2 2 2的个数为 n u m k num_k numk,那么 C x y C_x^y Cxy的质因数 2 2 2的个数就是 n u m x − n u m y − n u m x − y num_x-num_y-num_{x-y} numxnumynumxy。如果这个值为正,那么就是偶数;否则就是奇数。于是, 01 01 01序列的情况就处理完了。


注意到,这个序列中可能还含有 2 2 2。我们继续大力分讨。

假设这个序列只有 0 0 0 2 2 2,显然最终答案只能是 0 0 0 2 2 2,不能是 1 1 1。但是,我们仍然可以将这个序列转化为 01 01 01序列的情况。即,我们将这个 02 02 02序列中每个数都除以 2 2 2,变成 01 01 01序列;最终的答案为 01 01 01序列的答案的两倍。

最后一种情况: 这个序列存在 0 , 1 , 2 0,1,2 0,1,2

可以发现,根据性质③中的"如果经历了一次变换,序列中存在 1 1 1,则答案不能为 2 2 2",得到答案只能为 0 0 0 1 1 1。于是,我们可以将 2 2 2变成 0 0 0,此时转化为 01 01 01序列就直接用上述方法去算,最终答案显然不变。


于是,这道神仙题就做完了。时间复杂度 O ( n ) O(n) O(n)

有必要总结一下。这题中,我们可以通过打表思考来发现三个性质,然后将模型简化为 01 01 01序列,再扩展为其他情况,最终通过找规律与组合数、数论巧妙地解决了本题。

作为一道 A G C AGC AGC B B B题,这是比较难的,但是毫无疑问,本题考查得十分综合,是一道不可多得的好题。

Code

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxl=1000005;

int n,ans=0;
int a[maxl],b[maxl],c[maxl],with_2[maxl];

int C(int x,int y)
{
    
    
	int now=with_2[x]-with_2[x-y]-with_2[y];
	if (now)  return 0;
	else return 1;
}

signed main()
{
    
    
	cin>>n;
	for (int i=1;i<=n;i++)
	{
    
    
		char x;
		cin>>x;
		a[i]=x-'0';
	}
	if (n<=2)  return cout<<abs(a[1]-a[2])<<endl,0;
	for (int i=1;i<=n-1;i++)  b[i]=abs(a[i]-a[i+1]);
	
	int flag=1;
	for (int i=1;i<=n-1;i++)
	{
    
    
		if (b[i]==1)  flag=0;
	}
	if (flag==1)
	{
    
    
		for (int i=1;i<=n;i++)  b[i]/=2;
	}
	for (int i=1;i<=n-2;i++)  c[i]=abs(b[i]-b[i+1]);
	n-=2;
	
	for (int i=1;i<=n;i++)
	{
    
    
		with_2[i]=with_2[i-1];
		
		int saver=i,cnt=0;
		while (!(saver&1))  saver>>=1,cnt++;
		with_2[i]+=cnt;
	}
	for (int i=1;i<=n;i++)  ans=(ans+C(n-1,i-1)*c[i])%2;
	if (flag==0)  cout<<ans<<endl;
	else if (flag==1)
	{
    
    
		if (ans==1)  cout<<2<<endl;
		else cout<<0<<endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Cherrt/article/details/109293938