--------------------------------------------------------------------------
A. And Then There Were K
解:给你一个数n,要求你输出一个数k。其中数n和数k有关系:n&(n-1)&(n-2)&..........&k = 0 ,其中&是和运算。
这题可以简单的打个表找到规律。所有的k都是2^x-1(x=1、2、3........)。
比如给你n等于17,那么你可以找到的符合2 ^x-1并且小于n的最大的k是 15 ,15是2^4-1。
照这个思路可以简单的写出来答案。
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
using namespace std;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t;cin>>t;
while(t--){
int n;cin>>n;
int fina=1;
while(fina<=n){
fina<<=1;
}
fina>>=1;
cout<<fina-1<<endl;
}
}
--------------------------------------------------------------------------
B1. Palindrome Game (easy version)
解:思维题,爱丽丝和鲍勃玩游戏(正常人谁玩这种游戏啊.jpg),给一个01字符串(保证至少一个0)。
从爱丽丝开始,每人可以执行2种操作:
第一种,把字符串中的0改为1,花费1元。
第二种,把字符串翻转过来,花费为0元。能进行第二种操作的前提是上一个人没有使用翻转操作,或者目前的字符串不8888888888888是回文字符串。
每个人都会按照最优的策略来行动,当字符串全部变为1时结束,问你最后谁会赢。输出赢得人的名字,如果平局就输出“DRAW”。
这题保证最开始给你的字符串是回文字符串。
按照题目的意思,一开始爱丽丝先走,并且字符串最开始是回文字符串,那么爱丽丝就只能够进行操作一。
根据字符串中0的数量的奇偶性,结果可能有两种。
如果0的个数为奇数:比如回文串10001。按照最优的策略来行动,爱丽丝可以改写中间那个数。10001 -》 10101。这时爱丽丝花费1元,轮到鲍勃,因为字符串此时为回文串,鲍勃只能继续操作一,也就是把 10101 变成 11101 或者 10111。这时鲍勃花费为1元。 再次轮到爱丽丝,因为这时字符串为11101或者10111,那么爱丽丝可以进行翻转操作,这时花费为0元,爱丽丝的总花费数还是1元。
鲍勃没办法,只能再进行操作一,因为上一步爱丽丝已经进行过翻转操作了。那么这时字符串只能从11101(或者10111)变成11111。 全部变为1,游戏结束。爱丽丝花费1元,鲍勃花费2元。鲍勃比较败家,于是爱丽丝赢了。
同理,如果字符串为1000001或者01010,反正只要0的个数为奇数,就一定是爱丽丝赢。
注意:1也是奇数,不过如果0的个数为1时,比如101。爱丽丝执行了第一次操作后游戏就结束了,这时候会是鲍勃赢。这是特殊情况。
如果0的个数为偶数:那么经过推导,我们可以知道一定是鲍勃赢。
比如 ,给定字符串为:100001 ,爱丽丝走第一步,变成110001(其他情况也行),爱丽丝总花费为1元。
注意,每个人一定会按照最优的策略来走,那么接下来鲍勃的最优策略不是进行翻转操作,而是进行操作一,把字符串变成110011,这时鲍勃总花费为1元。
下一步爱丽丝没办法进行翻转操作了,只能继续操作一,于是可以把字符串从110011变成111011,这时爱丽丝总花费为2元。
这时我们的小婊砸鲍勃就可以阴险的进行操作二,翻转字符串,花费为0元。
下一步我们的爱丽丝没办法进行翻转了,只能再进行操作一,于是字符串由111011变成111111。这时候爱丽丝总花费为3元。
于是爱丽丝花了3元,鲍勃花了1元,爱丽丝输了。
鲍勃的这种走法在0的个数是偶数时是最好的方法,如果用了其他的走法那么有可能平局。
于是我们可以知道,当0的个数是偶数时,总是鲍勃赢得,并且我们可以知道最后两人的总花费之差是3-1=2元。
无论最开始的回文串是什么样子,反正只要0的个数为偶数,那么永远是先走的爱丽丝输掉游戏,并且爱丽丝的花费一定比鲍勃要多2元。(注意:这个性质很重要,在下一题中会用到)
可以自己去举例子,只要0个数为偶数一定符合上面的情况。
于是我们可以总结:0的个数为奇数,爱丽丝赢;0的个数为偶数或者0的个数为1,鲍勃赢。那么这题就简单了。
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
using namespace std;
string s;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t;cin>>t;
while(t--){
int n;cin>>n;
cin>>s;
int cnt=0;
for(int i=0;i<n;i++){
if(s[i]=='0')cnt++;
}
if(cnt%2==0||cnt==1)cout<<"BOB"<<endl;
else cout<<"ALICE"<<endl;
}
}
--------------------------------------------------------------------------
B2. Palindrome Game (hard version)
解:
这题和上一题一样,只不过不能保证最开始给你的字符串是回文串了。
如果最开始给你的还是回文串,那么按照上一题的做法来就可以了。
如果不是回文串,那么这题就开始有意思了。
先抛出结论:只要不是一开始不是回文串,那么要么是爱丽丝赢,要么是平局。反正鲍勃不可能赢。
爱丽丝赢麻了。
根据上一题的性质,如果是一个0的个数为偶数的回文串,那么先走的人一定会输,并且花费数之差为2。
如果你是爱丽丝,现在最开始的字符串不是回文串。因为是你先走,你现在可以自由的行动,进行操作一或者操作二都可以。你会用什么方法来干掉鲍勃?
根据上面的性质,我们可以想办法把字符串变成0的个数为偶数的回文串,并且让鲍勃先走,这样你一定会赢鲍勃。
比如:最开始的字符串为 100000 。 你可以先让字符串变成 100001 。下一步到鲍勃走了。
这时的情况就是,这个字符串已经被你变成个数为偶数的回文串了,并且鲍勃先走,于是就符合上面的情况了。最后鲍勃的花费比你多2元,算上你一开始花费的1元。这时你们的花费之差为1元。你赢了,鲍勃输了。
如果最开始的字符串是这种情况。10000,那么你可以选择先翻转。轮到鲍勃了,鲍勃必须要把这个字符串变成回文串,不然你就可以一直执行翻转操作,不花一点钱,最后鲍勃一定会输。所有鲍勃没办法,只能把这个字符串变成回文串。
10000变成回文串,有两种情况。10001或者11011。
如果是前者,你可以把中间的0变为1。10001 -》 10101,这时0的个数为偶数,并且下一步到鲍勃走了,于是又符合上面的情况了。你最后会赢。
如果是后者,这时候字符串是11011,鲍勃已经花费了3元,你花费1元把字符串变成11111。好,结束,你又赢了。
于是我们可以得知,如果一开始为0的个数为奇数的非回文串,爱丽丝总是可以把这个字符串变成0的个数为偶数的回文串。这样做最后爱丽丝会赢。
如果一开始为0的个数为偶数的非回文串,爱丽丝可以先进行翻转按兵不动,等待时机把字符串变成0的个数为偶数的回文串,爱丽丝又赢了。
于是爱丽丝赢麻了。
注意一种特殊情况,100或者001。这时候两个人一定打平。
也就是除了上面100或者001的情况,其他情况爱丽丝一定赢。
代码:
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t;cin>>t;
while(t--){
int n;cin>>n;
string s;cin>>s;
int ok=1,cnt0=0,cnt1=0;
for(int i=0;i<s.size()/2;i++){
if(s[i]!=s[s.size()-i-1])ok=0;
if(s[i]!=s[s.size()-i-1])cnt1++;
}
if(ok){
int cnt=0;
for(int i=0;i<n;i++){
if(s[i]=='0')cnt++;
}
if(cnt%2==0||cnt==1)cout<<"BOB"<<endl;
else cout<<"ALICE"<<endl;
}
else{
for(int i=0;i<n;i++){
if(s[i]=='0')cnt0++;
}
if(cnt0==2&&cnt1==1)cout<<"DRAW"<<endl;
else cout<<"ALICE"<<endl;
}
}
}
--------------------------------------------------------------------------
C. Sequence Pair Weight
解:给你一个数组,要你把数组中所有子串相同的无序对的个数加起来,然后输出。
举个例子: 1 2 1 。
这个数组的子串有 1 2 、 2 1 、 1 2 1 。
1 、2中 权值相同对数为 0。 2、1中权值相同对数为0。1 2 1 中权值相同对数为1。于是总数为1。
我们可以使用 布辱特佛斯(brute force)来做这道题。
我们一边输入一边计算答案。比如我们输入到第三个元素。1 2 1 。
我们把子段 1 2 1 、2 1、的和加入到ans中去。
同理,到第四个元素,1 2 1 2 。我们把子段 1 2 1 2、2 1 2 、 1 2 的和加入到ans去。
最后输出ans就可以了。
那么如何计算字段中权值相同无序对的和呢?
简单理解,我们可以用 dp[i] 表示到达当前元素的所有子段的和。可以发现 dp[ i ] = dp[ i-1 ] + m[ a[i] ]。 dp[ i-1 ] 表示从1到上一个元素为止的所有子段的和。
m[ a[i] ] 表示: 比如当前元素为1 ,假设之前还有3个1 。 那么 m[ 1 ] = 3 + 2 + 1。
注意在当前元素i结束后到下一元素i+1 时 ,要把m[ a[ i ] ] 加 ,变成 m[ 1 ] = 4 + 3 + 2 + 1。
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
using namespace std;
const int N=1e5+7;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t;cin>>t;
while(t--){
int n;cin>>n;
map<ll,ll> m;
ll ans=0,note=0;
for(int i=1;i<=n;i++){
ll tmp;cin>>tmp;
note+=m[tmp];
m[tmp]+=i;
ans+=note;
}
cout<<ans<<endl;
}
}
--------------------------------------------------------------------------