Codeforces Round #824 (Div. 2)

A. Working Week

题目链接:Dashboard - Codeforces Round #824 (Div. 2) - Codeforces

样例输入: 

3
6
10
1033

样例输出:

0
1
342

等价题意:给定一个长度为n的环形区域,我们可以任意选择三个方格放置一个‘X’,但是‘任意两个X’不能相邻,这样整个环形区域就被划分为3个区域,计算这三个区域中任意两个区域的长度差的绝对值,选绝对值最小的值作为答案,现在要最大化这个值,求这个值的最大可能值是多少?

分析:贪心来想,我们直接在第二个位置和第n个位置各放置一个‘X’,然后直接在中间某个位置再放置一个‘X’即可完成构造,一种最优的情况就是让其区间长度尽可能构成一个等差序列,然后我们的答案就是公差,现在我们只需要二分这个公差即可,当然也可以直接O(1)求出来,但是不如O(logn)实现起来简单,所以我是二分公差来做的。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int N=2e5+10;
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d",&n);
		int l=0,r=1e9;
		while(l<r)
		{
			int mid=l+r+1>>1;
			if(2+1+(1+mid)+1+(1+2*mid)<=n) l=mid;
			else r=mid-1;
		}
		printf("%d\n",l);
	}
	return 0;
} 

B. Tea with Tangerines

题目链接:Problem - B - Codeforces

样例输入: 

3
5
1 2 3 4 5
1
1033
5
600 900 1300 2000 2550

样例输出:

10
0
4

题意:一开始给定n个数,我们可以选择其中的数进行操作,每次操作是将x分解为两个正整数y和z使得y+z=x,也可以对分解后的数再进行分解,现在我们需要操作后的m个数(m>=n),满足任意两个数中的较大值都不会大于等于较小值的2倍,问最小操作次数。

分析:容易想到,我们可以先求一下n个数中的最小值mi,这个最小值肯定是不用进行操作的,我们只需要把所有的数分解为小于2*mi的数即可,换句话说就是看一下待分解的数与2*mi-1的关系即可,如果(k+1)*(2*mi-1)>=x>k*(2*mi-1),那么所需要的最小操作次数就是k,直接O(n)统计一下答案即可。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int N=2e5+10;
int a[N];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d",&n);
		int mi=0x3f3f3f3f;
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]),mi=min(mi,a[i]);
		long long ans=0;
		mi=2*mi-1;
		for(int i=1;i<=n;i++)
			ans+=((int)ceil(1.0*a[i]/mi)-1);
		printf("%lld\n",ans);
	}
	return 0;
}

C. Phase Shift

题目链接:Problem - C - Codeforces

 样例输入:

5
1
a
2
ba
10
codeforces
26
abcdefghijklmnopqrstuvwxyz
26
abcdefghijklmnopqrstuvwxzy

样例输出:

b
ac
abcdebfadg
bcdefghijklmnopqrstuvwxyza
bcdefghijklmnopqrstuvwxyaz

等价题意:给定一个长度为n的字符串,让我们制定一种映射规则使得将所给字符串映射后的字符串的字典序最小,输出映射后的字符串即可。映射规则需要满足是一个长度为26的环形,如a->b->c->……->z->a。

分析:这道题我们直接贪心来做即可,就是从左到右遍历,每次发现一个还没有制定映射规则的字符我们都将其映射为其可以映射范围内的字典序最小的字符串,什么叫可以映射的范围呢,,这样一定是最优的。

需要注意以下问题:对于当前的字符x还没有给出映射规则,假如还没有字符映射a,原则上我们是可以将x映射为a的,但是需要注意如果存在一条映射规则是a->……->x,但是这个链的长度并不是26,那么我们并不能将x直接映射到a,因为一旦这样映射就会产生一个环,那么剩余的元素将无法加入环中,所以这样映射是不合法的。

也就是说我们只需要在贪心的基础上排除这种不合法的情况即可。

细节比较多,详情见代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int N=2e5+10;
char s[N];
map<char,char>mp,mmp;
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d",&n);
		scanf("%s",s+1);
		mp.clear();
		mmp.clear();
		for(int i=1;i<=n;i++)
		{
			if(mp[s[i]]) printf("%c",mp[s[i]]);
			else
			{
				for(char t='a';t<='z';t++)
				{
					if(t==s[i]) continue;
					if(!mmp[t])
					{
						char tt=t;
						int cnt=0; 
						while(tt&&tt!=s[i])
						{
							cnt++;
							tt=mp[tt]; 
						}
						if(tt==s[i]&&cnt<25) continue;
						printf("%c",t);
						mp[s[i]]=t;
						mmp[t]=mp[s[i]];
						cnt++;
						break;
					}
				}
			}
		}
		puts("");
	}
	return 0;
}

D. Meta-set

题目链接:Problem - D - Codeforces

 样例输入:

9 2
0 0
0 1
0 2
1 0
1 1
1 2
2 0
2 1
2 2

样例输出:

54

题意:给定n张卡片(n张卡片中没有两张卡片是完全相同的),每张卡片有k个属性,每个属性的取值为0,1,2中的一个,我们定义3张卡片为一组,5张卡片为一个集合,一组被定义为good当且仅当这3张卡片的每一个对应属性要么全相同要么全不同,问满足一个集合至少有两组是good的集合数。

分析:我们先考虑什么样的集合能够满足至少有两组是good,能够发现我们选出两组good的组,如果这两个组有一个卡片是公共的,那么我们直接把这两组合到一起然后去掉一张公共的卡片即可得到一个好的集合。那如果要是没有公共卡片的是不是就意味着这两组good一定不能合成一个满足题意的集合呢?答案是YES,因为对于一个good的组,我们改变其中任意一个卡片后他都不可能再是good,因为任意两张卡片都可以唯一地确定第三张卡片,所以我们该变哪一张卡片都是不可以的,所以问题就转化为我们要求一张卡片所在的组是good的组的数目,由于题目中的k不是很大,而且对于每一组属性只有三种取值,所以我们可以直接用3进制数来存储这k张卡片的属性,两重for循环遍历任意两张卡片来求第三张卡片是否出现过即可,如果出现过就直接将3张卡片的贡献度都加1,但是我们发现这样会重复计算,也就是说比如第1,2,3张卡片可以合成一个good组,那么我们在枚举(1,2),(1,3),(2,3)时,这三张牌的贡献都会计算一次,所以我们需要将每张牌的贡献除以3得到不重复的贡献。最后我们直接for循环枚举每张卡片的贡献即可,对于包含某张卡片的任意两个组都可以组成一个good的集合,假如包含第i张卡片的组数是t,那么答案就是C(t,2),直接for循环统计一下所有的贡献即可。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int N=2e5+10;
map<long long,int> mp,ans;
int a[N][23],n,k;
long long cal(int row)
{
	long long t=0;
	for(int j=1;j<=k;j++)
		t=t*3+a[row][j];
	return t;
}
int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=k;j++)
			scanf("%d",&a[i][j]);
		mp[cal(i)]=1;
	}
	for(int i=1;i<=n;i++)
	{
		long long f1=cal(i);
		for(int j=i+1;j<=n;j++)
		{
			long long f2=cal(j);
			long long t=0;
			for(int l=1;l<=k;l++)
			{
				if(a[i][l]==a[j][l])
					t=t*3+a[i][l];
				else
					t=t*3+(3^a[i][l]^a[j][l]);
			}
			if(mp[t])
			{
				ans[f1]++;
				ans[f2]++;
				ans[t]++;
			}
		}
	}
	long long res=0;
	for(int i=1;i<=n;i++)
	{
		long long t=cal(i);
		ans[t]/=3;
		res+=ans[t]*(ans[t]-1)/2;
	}
	printf("%lld\n",res);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值