Codeforces Round #833 (Div. 2)

本文介绍了几道来自Codeforces编程竞赛的题目,涉及矩形拼接成正方形的最大边长计算、字符串中满足特定条件的子串计数、数组前缀和优化以及寻找特定整数解的问题。通过这些题目,展示了动态规划、前缀和、扩展欧几里得算法等在解决实际问题中的应用。
摘要由CSDN通过智能技术生成

A. The Ultimate Square

 

题目链接:Problem - A - Codeforces

 样例输入:

3
2
5
197654321

样例输出:

1
3
98827161

题意:给定n块矩形,第i块矩形的大小为[i/2]*1,现在给定n,让我们用这n块矩形去拼成一个正方形,问能拼成正方形的最大边长是多少?

分析:对于n是奇数的情况,我们直接对于所有的1<=i<=n/2,将第i块矩形和第n-i块矩形排成一排,然后第n块矩形单独一排,这样可以构成一个[(n+1)/2]*[(n+1)/2]的正方形,而如果n是偶数,我们可以发现第n块矩形用不上就可以构造出[n/2]*[n/2]的矩形,用上第n块矩形也没法使得正方形边长再大,面积上不允许。所以答案就是n除以2的上取整。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d",&n);
		printf("%d\n",(n+1)/2);
	}
	return 0;
}

B. Diverse Substrings

题目链接:Problem - B - Codeforces

样例输入:

7
1
7
2
77
4
1010
5
01100
6
399996
5
23456
18
789987887987998798

样例输出:

1
2
10
12
10
15
106

题意:给定一个由数字组成的字符串,问有多少个区间满足每种数字在区间内出现的次数都不大于该区间内不同数字的数目。

分析:容易发现一共有0~9这十种字符,那么也就是说每种字符出现的次数不能超过10,所以满足题意的区间长度也不可能超过100,所以我们直接枚举每个位置作为区间左边界满足题意的区间数即可。复杂度为o(100n)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=1e5+10;
char s[N];
int cnt[N];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d%s",&n,s+1); 
		int ans=0;
		for(int i=1;i<=n;i++)
		{
			int tt=0;//记录不同字符数 
			for(int j=0;j<=9;j++) cnt[j]=0;
			for(int j=i;j<=min(n,i+100);j++)
			{
				if(cnt[s[j]-'0']==0) tt++;
				cnt[s[j]-'0']++;
				bool flag=true;
				for(int k=0;k<=9;k++)
					if(cnt[k]>tt)
					{
						flag=false;
						break;
					}
				if(flag) ans++;
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

C. Zero-Sum Prefixes

题目链接:Problem - C - Codeforces

样例输入: 

5
5
2 0 1 -1 0
3
1000000000 1000000000 0
4
0 0 0 0
8
3 0 2 -10 10 -30 30 0
9
1 0 0 1 -1 0 1 0 -1

样例输出:

3
1
4
4
5

题意:定义一个数组的得分为满足前缀和等于0的下标i的个数,现在我们可以对数组进行任意次操作,每次操作可以将一个值为0的数变为任意值,使得最终得分尽可能大,输出最大得分。

分析:我们首先记录一下0所在的位置,我们可以发现,如果只考虑前面的数来调整当前0的值,那么我们最多使得答案加1,而如果考虑0后面的数,那么我们可以记录一下出现前缀和次数最大的值cnt,以及其对应的值id,那么如果id不为0,那么我们可以令这个0变为-id,那么答案就能增加cnt,如果id=0,那么我们cnt不变,可以使得答案增加cnt+1个。最后再考虑一下第一段非0区间的贡献即可

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=2e5+10;
int a[N],p[N],tt;
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d",&n);
		tt=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			if(a[i]==0) p[++tt]=i;
		}
		p[++tt]=n+1;
		int ans=0;
		for(int i=1;i<tt;i++)
		{
			long long t=0;
			map<long long,int>mp;
			int mx=0;
			for(int j=p[i]+1;j<p[i+1];j++)
			{
				t+=a[j];
				mp[t]++;
				if(t!=0)
					mx=max(mx,mp[t]);
				else
					mx=max(mx,mp[t]+1);
			}
			ans+=max(1,mx);
		}
		long long t=0;
		for(int i=1;i<p[1];i++)//考虑最前面一段非0区间
		{
			t+=a[i];
			if(t==0) ans++;
		}
		printf("%d\n",ans);
	}
	return 0;
}

D. ConstructOR

题目链接:Problem - D - Codeforces

样例输入:

8
12 39 5
6 8 14
100 200 200
3 4 6
2 2 2
18 27 3
420 666 69
987654321 123456789 999999999

样例输出:

18
14
-1
-1
0
11
25599
184470016815529983

题意:给定三个整数a,b,c,让我们找到一个x使得满足x<2^60,且a|x是d的倍数,b|x是d的倍数。输出d。( a和b以及c都是小于2^30的 )

分析:对于两个数u和v,假如u是v的倍数,那么u的二进制表示和v的二进制表示应该具有什么样的关系呢?其实u的二进制表示中1的最低位应该高于v的二进制表示中1的最低位。因为不妨假设u=k*v,那么对于k我们可以用二进制展开,那么就有u=((2^p1)*v+(2^p2)*v+……+(2^pn)*v),那么对于2^pi,都是相当于v左移pi位,那么1的最低位只可能会变高,而不可能变低,所以有u的二进制表示中1的最低位应该高于v的二进制表示中1的最低位。

那么我们就把上述问题转化为数学问题来求解。

不妨设d=p*2^k,也就是d的低k位为0,那么我们令x的低30位为11111……100……0,其中0的个数是k个,那么这样就能满足a|x=b|x=x。不妨设x的高30位为q,那么就有x=q*2^30+(2^(30-k)-1)*2^k.

那么现在就是寻找一个q使得x%d=0,也就是q*2^30+(2^(30-k)-1)*2^k=m*(p*2^k)有解,两边同除以2^k有q*2^(30-k)+2^(30-k)-1=m*p.等价于(q+1)*2^(30-k)%p=1.那么假设v就是2^(30-k)关于p的逆元,那么就有(q+1)%p=v,而v是2^30以内的数,而且q和p是2^30以内的数,所以该同余式显然有解。因为2^(30-k)只含有质因子2,但是p是一个奇数,不含有质因子2,所以两者是互质的,我们可以用扩展欧几里得求解逆元。细节见代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
void extend_gcd(long long a,long long b,long long &x,long long &y){
    if(b== 0)
	{
    	x=1,y=0;
        return;
    }
    extend_gcd(b,a%b,x,y);
    long long tmp=x;
    x=y;
    y=tmp-(a/b)*y;
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		long long a,b,d;
		scanf("%lld%lld%lld",&a,&b,&d);
		long long q=d;
		int t=0;//记录d的连续0的个数
		while(q%2==0)
		{
			q/=2;
			t++;
		}
		if(a%(1<<t)!=0||b%(1<<t)!=0)
		{
			puts("-1");
			continue;
		}
		long long v,vv;
		extend_gcd(1<<(30-t),q,v,vv);
		v%=q;
		long long p=(v-1+q)%q;
		printf("%lld\n",(p<<30)+((1<<(30-t))-1)*(1<<t));
	}
	return 0;
}

E. Yet Another Array Counting Problem

题目链接:Problem - E - Codeforces

这道题涉及到笛卡尔树,所以单独写了一篇博客进行讲解,有兴趣的小伙伴可以移步至该博客学习:

(1条消息) Codeforces Round #833 (Div. 2)E. Yet Another Array Counting Problem(笛卡尔树+树形DP)_AC__dream的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值