csp_M2 补题

HRZ的序列(set)

在这里插入图片描述
样例输入
2
5
1 2 3 4 5
5
1 2 3 4 5
样例输出
NO
NO

在这里插入图片描述

解题思路
1.我刚开始犯了个很大的错误,我以为K是我们输入的,是已知的。但是其实不是,这题错了其实也是怪我没看清楚题目就写了,写完才发现K是没给出来的。
菜到安详,其他题也没时间写。。。

2.这道题有个非常简单的解法,就是用set。通过分析,我们可以知道,如果有3个以上的不同的数字,例如 1234,就是不可能有k能符合题意的。
只有3个是可能可以符合题意的,3个以下是绝对可以找到K的。
所以利用set的性质可以将相同的数字给过滤掉,只留下不同的数字,然后在size==3的时候,我们进行比较,如果(大数+小数)/2 == 中间数 ,那么就可以找到K。

代码实现

#include<iostream>
#include<set>
#include<algorithm>
#define inf 1000000000000005
using namespace std;
set<long long> s;
int t,n;
long long K;
long long a[3];

int main()
{
	ios::sync_with_stdio(0);
	cin>>t;
	for(int I=0;I<t;I++)
	{
		long long ai;
		cin>>n;
		for(int i=0;i<n;i++)
		{
			cin>>ai;
			s.insert(ai);
		}
		if(s.size() == 3)
		{
			int ind=0;
			long long amax = 0;
			long long amin = inf;
			for(set<long long>::iterator it=s.begin();it!=s.end();it++)
			{
				a[ind++] = *it;
				amax = max(amax,*it);
				amin = min(amin,*it);
			}
			double mid = (amax+amin) / 2;
			if(mid == a[1])  
			cout<<"YES"<<endl;
			else cout<<"NO"<<endl;
		}
		else if(s.size()<3) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
		s.clear();
	}
	return 0;
 } 

小结
下次我一定要看清题目想好思路再动手,想一个清晰的思路往往事半功倍 QAQ

HRZ学英语(尺取法)

在这里插入图片描述

在这里插入图片描述

样例输入
ABC??FGHIJK???OPQRSTUVWXY?
样例输出
ABCDEFGHIJKLMNOPQRSTUVWXYZ
样例输入
AABCDEFGHIJKLMNOPQRSTUVW??M
样例输入
-1

解题思路
1.这题一看就想到用滑动窗口来解决(尺取法)
2.固定窗口大小

代码实现

#include<iostream>
#include<string>
using namespace std;
string s;
int l = 0, r = 25;  //左右指针,固定窗口大小为26
int coun[50];       //每个字母出现的次数
int c_w;            //问号的个数
int ci;
int n;

bool Judge()  //如果问号的个数等于没有出现的字母的个数,那么就返回 0
{
	ci = 0;
	for (int i = 0; i<26; i++)
	{
		if (coun[i] == 0) ci++;
		if (coun[i]>1) return 1;
	}
	if (ci == c_w ) return 0;
	return 1;
}

bool que()
{
	for (int i = l; i <= r; i++)
	{
		if (s[i] != '?')
			coun[s[i] - 'A']++;  //更新窗口内每个字母出现的次数
		else
			c_w++;   //问号出现的次数
	}
	while (r<s.size())
	{
	//	cout<<"**"<<endl;
		if (Judge())
		{			
			if (s[l] != '?')
			coun[s[l] - 'A']--;   //左指针右移后的维护
			else c_w--;
			l++;   //窗口同时右移
			r++;
			
			if (s[r] != '?' && r<s.size()) //右指针右移后的维护
			coun[s[r] - 'A']++;
			else if(r<s.size() && s[r] == '?') c_w++;
		}
		else return 1;
	}
	return 0;
}
//ABC??FGHIJK???OPQR?TUVWXY?
//AABCDEFGHIJKLMNOPQRSTUVW??M
int main()
{
	ios::sync_with_stdio(0);
	cin >> s;
	
	if (!que()) cout << -1 << endl;
	else   //输出结果
	{
		for (int i = l; i <= r; i++)
		{
			if (s[i] != '?')
			{
				cout << s[i];
			}
			else
			{
				for (int j = 0; j<26; j++) //判断要用哪个字母代替问号,字典序最小,先搜到的肯定最小
				{
					if (coun[j] == 0) {
						s[i] = j + 'A';
						coun[j]++;
						cout << s[i];
						break;
					}
				}
			}
		}
	}
	return 0;
}

小结
当时我写完了这一题,已经可以开始调试了,但是我发现我的string类型cin不进去,出现了某种玄学bug(dev),赛后我重启了一下dev,发现又可以输入了 QAQ 这可真的是玄学。

咕咕东的奇妙序列

在这里插入图片描述

样例输入
5
1
3
20
38
56
样例输出
1
2
5
2
0

在这里插入图片描述

解题思路
这题很难。

一开始我想到用前缀和把1 12 123 1234 这样的一点点分开,比如a[0] = 1,a[1]=2,a[3]=3这样,但是发现 1e18的 数据实在是太大了,大到让人绝望,以至于数组开不到那么大,所以我的思路还没开始就结束了。。。。。
但是要把他的数据分组压缩的思想是没有错的,只是用前缀和压得还不够多。

随后发现,可以按最大位数分为一组,例如,第一组的最大位数是1,第二组的最大位数是2.。。。。
然后可以画出图来
在这里插入图片描述

第一组最大到9,第二组最大到99,第三组最大到999.。。。。。。

想办法记录不同三角形的面积。
要想算出三角形面积,就要求出梯形的面积。
梯形的面积公式为 (上底+下底)×高 / 2

用一个wid数组,记录每一个三角形的底边长度
w数组 记录每一个大三角形的面积

在这里插入图片描述

我们计算出上底和下底就好了。
上底可以由上一层三角形的下底得到,我们需要计算下底。
下底为 height * i + wid[i-1] ,height为这个梯形的高度,例如9,90,900,9000…
(因为1到9,有9行;10到99有90行;100到999有900行。。。
每行多一 个 i 位数 ,所以最后一行就是height * i )

上底为wid[i-1]

此时我们就算出来了梯形的面积
S=(wid[i-1] + (height . i + wid[i-1]) ) height /2
三角形的面积为 w[i-1] + S

接下来,我们可以判断输入的x是属于哪个三角形的,然后再进行二分答案找到他属于这个梯形的哪一行,再二分找到他属于这一行的哪一个位数层,例如1 2 3 4 属于 1位层,10 11 12 属于二位层,找到他属于哪个数字 (是一个整体的数字,例如100是一个数字)
然后再计算出他属于数字的哪一个字符 ,输出字符,得到答案。

代码实现

#include<iostream>
#include<cmath>
#include<string>
#include<cstdio>

typedef long long ll;
using namespace std;
//w[i]记录第i层的总数量 (面积)
ll w[50];
//wid[i]记录第i层的宽,便于求w[i]
ll wid[50];

//初始化
void ini()
{
	w[0]=wid[0]=0;
	//heigt -- 每层的高度 
	ll heigt=9;	
	for(int i=1;i<=50;i++)
	{
		w[i] = w[i-1] + ( (wid[i-1]+i)+(wid[i-1]+heigt*i) )*heigt/2; //三角的面积	
		wid[i]=wid[i-1]+heigt*i;
		heigt*=10; 
		if( w[i]>=1e18 ) break;
	}	
} 

bool com(ll mid,ll i,ll x)   
{
	ll y=( (wid[i-1]+i)+(wid[i-1]+mid*i) )*mid/2;
	if(y>=x) return 1;
	else return 0;
}

ll f1(ll x ,ll i)
{
	ll l,r,mid;
	l=1;
	r=(ll)9*pow(10,i-1);
	while(l<=r){
	mid=(l+r)>>1;
	if(com(mid,i,x)) r=mid-1;
	else {l=mid+1;}
	}
	return l;
}

ll f2(ll x,ll i)
{
	ll l,r,mid,sum=0;
	l=1;
	r=i;
	while(l<=r)
	{
		mid=(l+r)>>1;
		for(ll j=1;j<=mid;j++)
		{
			sum+=j*(9*pow(10,j-1)); //mid前占了多少个位置 
		}
		if(sum>=x) r=mid-1;
		else {	
		l=mid+1;
		}
		sum=0;
	}
	return l;
}


int main()
{


	ios::sync_with_stdio(0);
	ini();
	int q; 
	ll x;
	int flag; //记录x在哪一层三角形 
	cin>>q;

	for(int qi=1;qi<=q;qi++)
	{
   cin>>x;


	//查找 x 在第几层 
	for(int i=1;i<10;i++)
	{
		if(w[i]>=x)
		{
			flag=i; break;
		}
	}

	//找x在该层三角形的哪一个位置
	ll T = w[flag] - w[flag-1]; //梯形的大小

	ll Tx = x-w[flag-1];        //x在梯形上的面积 

	//二分查找 x 在 T 的哪一行 
	ll cs=f1(Tx,flag);

	ll Tx2=Tx- ((wid[flag-1]+flag)+(wid[flag-1]+(cs-1)*flag) )*(cs-1)/2; //在该行上的位置(在这个梯形上的面积减去他上一行在梯形上的面积)

	ll R = f2(Tx2,flag);  //算出x是几位数的 

	ll sum=0;
	for(int k=1;k<R;k++)
	{
		sum+= k*(9*pow(10,(k-1)));
	}

	Tx2 -=sum;  //算出在R位数这个位数区间的位置

	ll j1;
	//j1表示是这个位数区间的第几个数(不是字符)
	if(Tx2%R==0) j1=Tx2/R;
	else j1=Tx2/R+1;

	ll j2;
	
    //该数第几个位置上的数字(指向字符)
    if(Tx2%R==0) j2=R;
    else j2=Tx2%R;

	ll num=1*pow(10,R-1)+j1-1;  //还原这个数

	for(ll ii=1;ii<=(R-j2);ii++) num=num/10; 
	cout<<num%10<<endl; 

	}

	return 0;
}

小结
如果当时我有时间做这道题的话,我可能会选择暴力求解前3个点,因为这题太难了,写不完。

发布了31 篇原创文章 · 获赞 1 · 访问量 1039

猜你喜欢

转载自blog.csdn.net/Hanpi_learnc/article/details/105483516
今日推荐