WEEK8模测

此次模测依旧是针对CSP的T1,T2,T4,但是完成的不太好,有待改进,1题没有想到最简单的思路,仅仅靠特判拿到了60分,2题先用简单方法搞定了123点,然后开始写常规方法,但是时间太紧了没有调出来,直接交了拿了40分,事后发现只是未将滑动窗口的l和r指针移动罢了QAQ太尴尬了。3题没啥思路。。就靠贴数据想要骗到30分,结果还因为语言的问题导致了CE,还是需要多多磨炼。

T1 HRZ的序列

题目描述
对于一个序列,是否存在一个数K,使得一些数加上K,一些数减去K,一些数不变,使得整个序列中所有的数相等,其中对于序列中的每个位置上的数字,至多只能执行一次加运算或减运算或是对该位置不进行任何操作。
思路
如果可以完成的话,那么序列中至多只有三种数:a-k,a,a+k,如果出现三种数情况之外的数那么一定不满足题意。所以只要先排序求出最小值与最大值,然后得到a,再遍历对每个数进行判断即可。

#include <stdio.h>
#include <algorithm>
#include <string.h>

using namespace std;

int t,n,flag;
long long a[10005];

int main()
{
	scanf("%d",&t);
	while(t--)
	{
		flag=0;
		memset(a,0,sizeof(a));
		scanf("%d",&n);
		for(int i=0;i<n;i++)
			scanf("%lld",&a[i]);
		sort(a,a+n);
		long long mid=(a[0]+a[n-1])/2;
		for(int i=0;i<n;i++)
		{
			if(flag==1) break;
			if(a[i]!=a[0] && a[i]!=a[n-1] && a[i]!=mid)
			{
				flag=1;
				printf("NO\n");
			}
			else continue;
		}
		if(flag==0) printf("YES\n");
	}
	return 0;
}

T2 HRZ学英语(滑动窗口)

题目描述
现在给定一个字符串,字符串中包括26个大写字母和特殊字符’?’,特殊字符’?‘可以代表任何一个大写字母。现在TT问你是否存在一个位置连续的且由26个大写字母组成的子串,在这个子串中每个字母出现且仅出现一次,如果存在,请输出从左侧算起的第一个出现的符合要求的子串,并且要求,如果有多组解同时符合位置最靠左,则输出字典序最小的那个解!
数据范围
在这里插入图片描述思路
首先看到数据范围,123点字符串长度只有26,可以利用简单的方法进行计算,判断,当前已确定字母的出现次数,如果有>1的则无法构成,如果均为1,那么按照字典序给’?‘赋值即可。(实际写的时候这个可以省略)
之后的数据点则利用位置连续的特点,采用滑动窗口的方式,窗口长度为26,判断当前窗口内的字母是否只出现一次,以及?的数量等于缺失的字母的数量。然后按照字典序给’?'赋值。

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <queue>

using namespace std;

char c[1000005];
int a[30];
int len;
bool flag;
int l=0,r=26;
//滑动窗口
queue<char> q;

bool right()
{
	for(int i=0;i<26;i++)
	{
		if(a[i]>1) 
		{//26个字母中已知的发生重复则不可 
			return false;
		}
	}
	//没有重复且?个数和与q.size相同,则输出
	for(int i=0;i<26;i++)
	{
		if(a[i]==0) q.push(i+'A');
	}
	if(a[26]==q.size()) 
		return true;
}


int main()
{
	cin>>c;
	len=strlen(c);
	if(len<=26)
	{//长度为26时
		for(int i=0;i<len;i++)
		{
			if(c[i]!='?')
				a[c[i]-'A']++;
			else a[26]++;
		}
		for(int i=0;i<26;i++)
		{
			if(a[i]>1) 
			{//26个字母中已知的发生重复则不可 
				cout<<"-1"<<endl;
				return 0;
			}
		}
		//没有重复
		for(int i=0;i<26;i++)
		{
			if(a[i]==0) q.push(i+'A');
		}
		for(int i=0;i<len;i++)
		{
			if(c[i]=='?')
			{
				char tmp=q.front();
				q.pop();
				c[i]=tmp;				
			}
		}
		cout<<c;
	}
	else
	{//使用滑动窗口
		for(int i=0;i<26;i++)
		{
			if(c[i]!='?')
				a[c[i]-'A']++;
			else a[26]++;
		}
		while(r<=len)
		{
			if(right())
			{
				flag=true;
				for(int i=0;i<26;i++)
				{
					if(c[i+l]=='?')
					{
						char tmp=q.front();
						q.pop();
						c[i+l]=tmp;				
					}
				}
				for(int i=0;i<26;i++)
				{
					printf("%c",c[i+l]);
				}
				return 0;
			}
			else 
			{
				if(c[l]=='?') a[26]--;
				else a[c[l]-'A']--;
				if(c[r]=='?') a[26]++;
				else a[c[r]-'A']++;
				l++;
				r++; 
				while(q.size()) q.pop();
			}
		}
		if(!flag) cout<<"-1";
	}
	return 0;
}

T3 咕咕东的奇妙序列

题目描述
一个奇怪的无限序列:112123123412345 …这个序列由连续正整数组成的若干部分构成,其 中第一部分包含1至1之间的所有数字,第二部分包含1至2之间的所有数字,第三部分包含1至3之间的所 有数字,第i部分总是包含1至i之间的所有数字。所以,这个序列的前56项会是 11212312341234512345612345671234567812345678912345678910,其中第1项是1,第3项是2,第20项是 5,第38项是2,第56项是0。咕咕东现在想知道第 k 项数字是多少
数据范围
在这里插入图片描述思路
首先这真是一个奇怪的题。考虑123点的数据范围,可以直接把题目中给的前56项先贴在代码里,然后直接O(1)查询即可。但是后面的点就要考虑实际如何操作了。
首先分析序列,可以写成这个样子:
1
12
123
1234
12345
123456
1234567
12345678
123456789
12345678910
1234567891011
123456789101112

先定义两个概念,

  • 第i组:最大值为i位数
  • 第j部分:第i组的第j个序列

首先我们的思想就是不断的减去k之前的有规律的组,从而确定k是当前第i组第j部分的第几个len位数,然后再进行查询。

  1. 初始化:
    a[i]表示第i组的位数之和,b[i]表示第i组最后一个序列的长度,count表示i位数序列的个数
    第1组的位数之和为:a[1]=(9+1)*9/2
    第2组的位数之和为:a[2]=[(9+2)+(9+2×90)]×90/2
    第3组的位数之和为
    a[3]=[(9+2×90+3)+(9+2×90+3×900)]×900/2

    第i组:a[i]=[(b[i−1]+i)+(b[i−1]+count×i)]×count/2

这个可以在最开始初始化得到,后续也无需更改。

  1. 确定k为第i组
    判断k与a[i]的关系,若k>a[i],则不在第i组,可以从k中减去a[i],直到k<=a[i]为止,那么k就在第i组。

(由于数据上限的限制,a[i]最大只到a[9],所以可以直接遍历比较,无需二分)

  1. 确定k为第i组的第j部分
    这个的确定方法与刚刚确定组数的方法一致,我们可以二分求得所求位置所在串的最后一个数是多少,即k为第i组的第j部分。由于每组的序列数较多,所以我们利用二分的方法进行查找。

  2. 确定k在该序列中的位置
    同样的,方法还是分组,然后从k中减去,直至确定k所在的实际数据的位数,设为len。求出所在数据为第num个len位数,减去前面的num-1个数字即为当前数据。然后判断k是该数据的第几个数,通过/10的方法获得,最后%10即为k所代表的的元素。

#include <math.h>

using namespace std;

long long k,a[20],b[20];
long long q,weishu;

void initial()
{//记录i位数所在组数的最大位数
	long long count=9;//i位数的序列个数
	a[0]=b[0]=0;
	for(long long i=1;i<=9;i++)
	{//i为9的时候位数会超过1e18
		a[i]=((b[i-1]+i)+(b[i-1]+count*i))*count/2;//i位数的上界
		b[i]=b[i-1]+count*i;//最后一个i位数所对应的序列长度
		count*=10;
		printf("%lld\n",a[i]);
	}
}

int main()
{
	scanf("%d",&q);
	initial();
	while(q--)
	{
		scanf("%lld",&k);
		//确定k的位数
		for(int i=1;i<=9;i++)
		{
			if(a[i]>=k) 
			{
				weishu=i;
				break;
			}
			else k-=a[i];
		}
		printf("%lld\n",k);
		//二分查找是第i位数序列的第几组
		long long l=1,r=9*pow(10,weishu-1),mid;
		long long sum;//k所在组之前的数据个数。
		while(l<=r)
		{
			mid=(l+r)/2;//mid为第i位数序列的第mid组,即名次
			sum=((b[weishu-1]+weishu)+(b[weishu-1]+mid*weishu))*mid/2;
			if(sum>=k) r=mid-1;
			else l=mid+1;
		}
		//r即为k所在组的名次,将之前的sum减去
		k-=((b[weishu-1]+weishu)+(b[weishu-1]+r*weishu))*r/2;
		//k变为该组中的名次,直接查找
		long long len=1;
		while(1)
		{//将范围缩小到该组的len位数
			long long pre=9*pow(10,len-1)*len;//在k之前的数字的个数
			if(pre>=k) break;
			k-=pre;
			len++;
		}
		long long num=(k+len-1)/len;//求出是第num个len位数
		//减去前面num-1个
		k-=(num-1)*len;
		//第num个len位数的大小
		num=pow(10,len-1)+num-1;
		long long cnt=len-k+1;//k为该len位数的第cnt位
		for(long long i=1;i<cnt;i++) num/=10;
		printf("%lld\n",num%10);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值