2021牛客练习赛90

2021牛客练习赛90

B.寒冬信使

题目链接:https://ac.nowcoder.com/acm/contest/11180/B
在这里插入图片描述
在这里插入图片描述
code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    int t;
    cin>>t;
    for(int i=1;i<=t;i++)
	{
		string s;
		cin>>s;
		ll sum=0;
		for(int i=0;i<s.length();i++)
		{
			if(s[i]=='1')
			sum+=i+1;
		}
		if(sum%2==1)
		cout<<"T"<<"\n";
		else cout<<"X"<<"\n";
	}
	
    return 0;
}

这道题是个博弈题,一道很有趣的题目。题目的大意是:两个人轮流选一个白色格子并翻转它,以及翻转这个白色格子的前一个格子(翻转指得是变成相反的颜色,即白变黑,黑变白),无法操作者败,如果先手的人一定能胜则输出"T",否则输出"X"。这题的解法我很快就想出来了,但一直犹豫要不要交上去,因为感觉这样写太简单了,而且当时只有80个人写出来了,所以我犹豫了10分钟,等到我试了很多特殊例子后都是正确的我才交了,然后过了。都是题外话,现在说正解。我的想法是如果总共操作了奇数次,那么先手必赢。所以我统计了字符串中每个1的位置,并将它们加起来,如果是奇数,则输出"T",否则输出"X"。那么为什么可以这样写呢?我举个例子吧,首先如果操作时不存在两个1靠在一起的情况,比如0101,从左往右依次把1变成0,那么总共操作了偶数次,2+4=6,那么先手必败(这个应该不用我解释吧,模拟试试就知道了),如果操作时存在两个1靠在一起的的情况,还是拿之前的0101举例子,0101->0110->0000,在这个过程中我们可以发现,两个1靠在一起的情况所贡献的操作次数还是奇数,毕竟i+(i+1)=2*i+1为奇数,加上之前的一次操作总共就是偶数次,于是我们就可以有理由的得出一个结论:位置相加的奇偶数情况决定输出情况。

C.盾与战锤

题目链接:https://ac.nowcoder.com/acm/contest/11180/C
在这里插入图片描述
在这里插入图片描述
code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+10;
ll a[N],b[N];
bool compare(ll x,ll y)
{
    return x>y;
}
int main()
{
    ll n,s;
    cin>>n>>s;
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    ll ans=0; 
    sort(a+1,a+1+n,compare);
    for(int i=1;i<=2*n;i++)
    {
        b[i]=b[i-1]+a[i];
    }
    for(ll k=1;k<=n;k++)
    {
        ans=0;
        for(ll t=0;;t++)
        {
           ans=max(ans,b[t*k]-t*s);
           if(t*k>=n) break;
        }
        printf("%lld\n",ans);
    }   
    return 0;
}

这道题我一直无法理解这里的子序列的定义。题目中也不给个解释,于是用了错误的理解一直找不出来解决方法。我的理解是相对位置不变,也就是说在原序列5,2,4,1中可以选5,2,4,但不能5,4,2,而答案却是可以的。。。这道题的解法是:对于每个k,枚举破盾数t,范围是0~n/k,但其实破盾数最大可为n/k+1,这就要在枚举中做些小优化了,具体见代码:

 for(ll t=0;;t++)
 {
     ans=max(ans,b[t*k]-t*s);
     if(t*k>=n) break;
 }

然后把前最大的t✖k个数相加减去t✖s,就是当前破盾数所对应的伤害,然后更新最大的伤害即可,我们可以用前缀和预处理前t✖k大的数。这里要注意的是数组的长度和前缀和循环的范围须定义为2倍的n的最大值,即2e6+10,因为如果是1e6+10的话,数组会越界的。那么为什么会越界呢?假设k=n-1,那么按照我们上述写法,当t=2时,2*k就是2✖(n-1)了,所以必须定义为2e6的级别,那么,这就有人回问了,我们别这样判断了,直接这样写不就行了吗:

for(ll t=0;t*k<=n;t++)
 {
     ans=max(ans,b[t*k]-t*s);
 }

你一交就会发现只过了30%的例子,这是为什么呢?其实就是我在上面所说,t是可以为n/k+1的!也就是说我们必须得循环到第一个大于或等于n/k的数才能退出循环。那么为什么可以为n/k+1呢?举个例子你就清楚了,n=10,k=4的时候:
在这里插入图片描述
五角星为破盾时所在的位置,1~10这些数为从大到小排序后的数所在的位置,从这张图我们可以看出有三个破盾点,而不是10/4=2了,最后一个破盾后所选取的范围已经超过了n了,也能解释为什么数组大小为n会越界了。
还有一点要说明的是这种做法的时间复杂度是O(nlogn),证明见下图:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值