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),证明见下图: