Codeforces Round #721 (Div. 2) ABC题解(c++实现)

--------------------------------------------------------------------------

A. And Then There Were K

链接:Problem - A - Codeforces

解:给你一个数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)

链接:Problem - B1 - Codeforces

解:思维题,爱丽丝和鲍勃玩游戏(正常人谁玩这种游戏啊.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)

链接:Problem - B2 - Codeforces

解:

这题和上一题一样,只不过不能保证最开始给你的字符串是回文串了。

如果最开始给你的还是回文串,那么按照上一题的做法来就可以了。

如果不是回文串,那么这题就开始有意思了。

先抛出结论:只要不是一开始不是回文串,那么要么是爱丽丝赢,要么是平局。反正鲍勃不可能赢。

爱丽丝赢麻了。

根据上一题的性质,如果是一个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

链接:Problem - C - Codeforces

解:给你一个数组,要你把数组中所有子串相同的无序对的个数加起来,然后输出。

举个例子: 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;
	}
}

 

--------------------------------------------------------------------------

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值