AtCoder Regular Contest 145 A-D题解

目录

A - AB Palindrome

B - AB Game

C - Split and Maximize

D - Non Arithmetic Progression Set


A - AB Palindrome

给出一个由A和B构成的字符串,可以用AB替换其中的连续两个字符,并且可以替换多次,问是否能最终成为回文

由于可以替换多次,所以我们可以一点点想   

无非就以下八种情况

A**A(总长为偶数),A***A(总长为奇数),B**B,B***B,B**A,B***A,A**B,A***B

A**A-> AB*A-> AABA-> ABBA

A***A-> AB**A-> ABABA

B**B-> BABA->BAAB

B***B-> BAB*B->BABAB

B**A-> BABA-> BAAB

B***A-> BABAB

而其余两种情况由于我们不能改变s0的A,也不能改变末尾的B,所以一定无解

综上总结一下,B开头A结尾除了BA外都可

代码如下

string s;
int n;
signed main()
{
    cin>>n;
	cin>>s;
	if((s[0]=='A'&&s[n-1]=='B')||(s=="BA")) cout<<"No";
	else cout<<"Yes";
}

B - AB Game

博弈论,说一共N回合,每回合有n个石头(1<=n<=N)Alice只能拿走A的倍数的石头,Bob只能拿走B的倍数的石头,问在双方都发挥最佳情况下,Alice赢多少局

首先我们分情况想

由于我们先拿,如果总的石头n小于a,那我们无论如何第一回合也拿不到,所以这种情况下我们是必输的,答案为 0

然后就是n大于a时候,也有两种情况,b大于等于a和b小于a,其中b>=a的情况是比较好考虑的

我们第一回合可以拿n/a *a个,使石头堆里剩余n%a个,n%a<=a 那他必然<=b,Bob就是必输的

所以n大于a且b大于等于a时 我们除了第一种情况(n<a)的区间,我们都是必赢的

所以这种情况答案是   n-a+1

之后就是最复杂的a>b,我们可以想象,如果我们拿了一次p*a之后,剩下的数小于b了,那Bob就输了,画成数轴就是这样

也就是对于任意p\subset \left ( 1,2......(n/a-1)) \right ) (因为要抛去第一段0-a,这段我们是必输的,所以-1)

 我们可以赢 [p*a-p*a+b) 个回合,也就是b个

所以答案是(n/a(合法p的个数) -1)* b 

还漏掉最后一块也就是 n%a 这一段,而这一段又有两种情况

一种是n%a大于等于b,一种小于b

而对于上面情况 结合以上分析,我们只可以赢 [p*a,p*a+b)个回合,也就是b个

下面情况 我们可以赢 n%a+1 个回合

所以最后答案要再加上一个

min(n%a+1,b)

三种情况讨论完,代码如下 

int n,a,b;
signed main() {
	cin>>n>>a>>b;
	if(a>n) cout<<0;
	else if(a<=b) cout<<n-a+1;
	else cout<<(n/a-1)*b+min(n%a+1,b);
}

C - Split and Maximize

题目说让我们把1-2*N 这个排列分成两组长为N的序列,使得他们的叉积最大,问有多少种排列方式可以得到这个最大的叉积

首先我们要明白一个前置条件

当 A<B<C<D 时

有 A*D + B*C < A*C + B*D < C*D + A*B

例如1234吧    10<11<14

我们推广到2*N的序列,不难想象这个最大的叉积是由

(2*N)*(2*N-1)+(2*N-2)*(2*N-3).........2*1

也就是要2*N和2*N-1对应,2*N-2要和2*N-3对应.....

这就转化为一个排列组合问题

我们知道,一共有N个空位,一旦A中的第i位确定了,则B中第i位也被确定(因为要一一对应)

所以对于每个空位,总的排列有2的N次幂种

而对于N个空位,每个位置依次选择,有\frac{1}{n+1}\binom{2*N}{N}种选择

所以答案为2^{N}\frac{(2*N )!}{(N+1)!}

代码如下

const int mod=998244353;
int qpow(int a,int b,int p){
    int res=1%p;
    while(b){
        if(b&1) res=res*1ll*a%p;
        a=a*1ll*a%p;
        b>>=1;
    }
    return res;
}
signed main() {
    int n;
    int ans=1;
	cin>>n;
    ans=qpow(2,n,mod);
	for(int i=n+2;i<=2*n;i++){
        ans=ans*i;
        ans%=mod;
    }
	cout<<ans<<endl;
	return 0;
}

D - Non Arithmetic Progression Set

题意为给你个N,给一个M,让我们构造一个长度为N的序列,使得总和为M

并且对于任意三元组 y-x!=z-y

我们可以变幻以下式子,给他变成 2*y=x+z

也就是对于任意三元组,任意两个数加起来不等于x+z

怎么构造呢,我们可以基于三进制考虑

如果我们在三进制情况下,选择只由0和1构成的数(三进制中只有012)

这样2*y在三进制下就会只由0和2构成

而对于x+y,也只由2和0构成的数,因为原数字由01构成,加法时没有进位

这样要想x+z==2*y,只有x==y==z时才能成立,也就是我们选择不同的这样的数,构造出来的序列就天然满足条件

接下来就是考虑M的限制了

我们可以在最开始的第一个数从0开始选,这样以后加或减的时候得数仍在-1e7-1e7之间

然后我们通过调整数字,就可以满足m

这题代码有点抽象

const int N=10010;
int n,m,a[N];
signed main(){
	scanf("%lld%lld",&n,&m);
	int sum=0;
	for(int i=0;i<n-1;i++){
		for(int j=0,b=1;j<15;j++,b*=3){
            if(i>>j&1){
                a[i+1]+=b;
            }
        }
		sum+=a[i+1];
	}
	int r=(m-sum)%n;
	a[n]=(a[n-1]*3+n)/n*n+r;
	int d=(m-a[n]-sum)/n;
	for(int i=1;i<=n;i++){
        cout<<a[i]+d<<endl;
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值