A.小红的正整数自增
#include <iostream>
#include<algorithm>
using namespace std;
void Solved() {
int x;
cin>>x;
int t=x%10;
if(t==0) cout<<0<<endl;
else cout<<10-t<<endl;
}
int main()
{
int t;
t=1;
while(t--) {
Solved();
}
return 0;
}
B.小红的抛弃后缀
这题有两种做法一个是结论,一个是直接用高精度思想直接模拟。
一,方法一:结论
结论就是和3这个类似,当一个数的数位和可以被9整除那么这个数就能被9整除如:18。
#include <iostream>
#include<algorithm>
using namespace std;
void Solved() {
string str;
cin>>str;
int cnt=0;
int sum=0;
for(int i=0;i<str.size();i++){
sum = (sum +(str[i]-'0'))%9;
if(sum==0) cnt++;
}
cout<<cnt<<endl;
}
int main()
{
int t;
t=1;
while(t--) {
Solved();
}
return 0;
}
二,方法二:高精度思想模拟
思路直解根据高精度思想模拟出一个数除以9的计算过程即可
#include <iostream>
#include<algorithm>
using namespace std;
void Solved() {
string str;
cin>>str;
ll cnt=0;
//表示余数
int t=0;
//高精度思想
for(int i=0;i<str.size();i++){
int num=t*10+str[i]-'0';
if(num%9==0) cnt++;
t=num%9;
}
cout<<cnt<<endl;
}
int main()
{
int t;
t=1;
while(t--) {
Solved();
}
return 0;
}
C.小红的字符串构造
也是两种方法一种是构造全是一样字母结合分求解,一种是根据题目的数据范围k<=n/2,可以联想到aabbccaabbcc这种构造方法。这两个方法都利用了一个性质就是独立贡献互不影响。
如:aabb----->aa是一个回文,bb是一个回文,他们互不干扰。像aabbaa,这个就不行会相互影响。所以我们可以aabbccaabbcc这样构造他们就不会相互影响了。
方法一:
1.思路:我们可以分析 aa aaa aaaa aaaaa ····,他们的回文数量分别是 1 3 6 10 15。他们的回文数量我们会发现是有规律的,因此我们可以打一个表再利用二分来寻找构造的字母。
2.代码:
#include <iostream>
#include<algorithm>
#include<cstring>
#include<set>
#include<stack>
#include<queue>
#include<map>
using namespace std;
const int N=1e5+10,M=1e9+10;
typedef long long ll;
typedef pair<int,int> pii;
ll f[N];
void Solved() {
ll n,k;
cin>>n>>k;
//打表
//与之对应的是f[i]=x--->i+1为字符串长度,x为回文数量
ll stance=2;
f[1]=1;
for(int i=2;i<=n;i++){
f[i]=f[i-1]+stance;
stance++;
}
//保存结果字符串
string ans;
//用于使字符串中字母随字母表循序变化
char c;
int idx=0;
//通过二分来凑出k个回文
while(k>0){
ll l=0,r=n;
while(l<r){
ll mid=(l+r+1)>>1;
if(f[mid]<=k) l=mid;
else r=mid-1;
}
//减去已贡献的回文数量
k-=f[l];
//这次要拼接的字符
c='a'+idx;
//拼接l+1个c
for(int i=1;i<=l+1;i++) ans+=c;
//字母变化,在26个字母中循环变化
idx++;
if(idx>25){
c='a';
idx=0;
}
//减去已贡献的字符串长度
n-=l+1;
}
//当字符串回文数量满足条件,但是字符串长度不够时,在后面随机补字母
if(k==0&&n>0){
for(int i=1;i<=n;i++){
c='a'+idx;
idx++;
ans+=c;
if(idx>25){
c='a';
idx=0;
}
}
}
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
//cin>>t;
t=1;
while(t--) {
Solved();
}
return 0;
}
方法二:
1.思路:用aabbcc,这种构造方法来构造,因为题目数据范围限制使得,一定可以构造出。
2.代码:
#include <iostream>
#include<algorithm>
using namespace std;
void Solved() {
int n,k;
cin>>n>>k;
//预先定义出需要循环拼接的字母三个字母足以
string a="abc",b="def";
string ans;
for(int i=0,j=0;i<k;i++){
//两个相同的字母贡献一个回文
ans+=a[j];
ans+=a[j];
j=(j+1)%3;
}
//在字符串长度不够的时候,在后面随机补字母,前提不要贡献回文
int j=0;
while(ans.size()<n){
ans+=b[j];
j=(j+1)%3;
}
cout<<ans<<endl;
}
int main()
{
int t;
t=1;
while(t--) {
Solved();
}
return 0;
}
D.小红的平滑值插值
思路:这题其实我们只要发现一个性质就可了,要使的某一个成为最大值只需要把,比他大的变得小于等于他即可。当时在这题该如何将差距最大值变小呢?举个例子:3 1 5 我们要使差距为2,所以我们只需要在1和5之间插入一个3即可,那么将 5改成6呢?可以在1 6之间插入 3 5。只要保证我插入的数之间两两的差距小于等于2即可。
代码:
#include <iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10,M=1e7+10;
typedef long long ll;
typedef pair<int,int> pii;
int arr[N];
void Solved() {
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>arr[i];
int mx=0;
ll sum=0;
for(int i=2;i<=n;i++){
int num=abs(arr[i]-arr[i-1]);
mx=max(mx,num);
//当你本来就比目标值小就不需要操做了
if(num<=k) continue;
//利用数学取余向上取整就能很好的计算出他们之间应该插入的数字数量了
sum+=(num+(k-1))/k-1;
}
//当差距最大值都比目标值小时,不管值为多少,都只需要插入一个数字即可。
if(mx<k) sum=1;
cout<<sum<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
t=1;
while(t--) {
Solved();
}
return 0;
}
E.小苯的等比数列
思路:因为是等比数列,且数据范围比较小,所以我们完全可以枚举每一个数的公比来求最大长度。再加一点剪枝。
代码:
#include <iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=4e5+10,M=1e7+10;
typedef long long ll;
typedef pair<int,int> pii;
int arr[N];
int cnt[N];
void Solved() {
int n;
cin>>n;
//去重
set<int> s;
//元素最大值
int mx=0;
int ans=0;
for(int i=1;i<=n;i++){
int x;
cin>>x;
mx=max(mx,x);
s.insert(x);
cnt[x]++;
//预先处理一下公比为1的情况
ans=max(ans,cnt[x]);
}
//枚举每个元素
for(auto x:s){
//枚举他所有公比
//当公比为2的时候 2^17>2e5,所以一个公比最多枚举17次,已经很小了。
//x*pow(i,ans-1)----->当你这个公比根据目前最长等比数列长度来计算,超过最大只是就直接pass
for(int i=2;x*pow(i,ans-1)<=mx;i++){
if(x*i>mx) break;
//当这个元素乘这个公比连第二项都没有,那么后面也就没有枚举的必要了
if(cnt[i*x]==0) continue;
//看他能往后面推多少项
int temp=0;
while(x<=mx&&cnt[x]!=0){
temp++;
x*=i;
}
ans=max(ans,temp);
}
}
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
t=1;
while(t--) {
Solved();
}
return 0;
}
F.小苯的回文询问
思路:这题要知道一个性质,就是我们要看一个子数组里面是否是好数组,只需要看他里面有没有两个一样的元素且他们不相邻如 121。那么怎么来判断呢?我们可以用一个last数组来记录一个数字他上一次出现在哪里(下标)。只要在一个区间 [ l,r ] 之间有一个元素的 last>=l 并且他们还不是相邻的,就满足条件。那么怎么快速的判断这个区间是否满足这个条件,其实只要这个区间的最大的last>=l,那么就满足情况。要快速获取一个区间之间的最大值我们可以用线段数来解决。
代码:
#include <iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<set>
#include<stack>
#include<queue>
#include<map>
using namespace std;
const int N=2e5+10,M=1e7+10;
typedef long long ll;
typedef pair<int,int> pii;
int arr[N];
int last[N];
//下面都是线段树求解最大值模板
struct Node{
int l,r;
int mx;
}tr[4*N];
void build(int u,int l,int r){
if(l==r) tr[u]={l,r,last[l]};
else{
tr[u]={l,r};
int mid=(l+r)>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
tr[u].mx=max(tr[u<<1].mx,tr[u<<1|1].mx);
}
}
int query(int u,int l,int r){
if(l<=tr[u].l&&r>=tr[u].r) return tr[u].mx;
int mid=(tr[u].l+tr[u].r)>>1;
int temp=0;
if(l<=mid) temp=max(temp,query(u<<1,l,r));
if(r>=mid+1) temp=max(temp,query(u<<1|1,l,r));
return temp;
}
void Solved() {
int n,q;
scanf("%d%d",&n,&q);
//map来记录上一次出现这个元素的位置
map<int,int> mp;
int x;
for(int i=1;i<=n;i++){
scanf("%d",&x);
if(mp.count(x)){
last[i]=mp[x];
}
mp[x]=i;
}
//先将相邻且相同元素情况给排除掉,比面影响后面线段树求解
for(int i=n;i>0;i--){
if(last[i]==i-1){
last[i]=last[last[i]];
}
}
//线段树求解区间最大值模板
build(1,1,n);
for(int i=1;i<=q;i++){
int a,b;
scanf("%d%d",&a,&b);
if(query(1,a,b)>=a) printf("YES\n");
else printf("NO\n");
}
}
int main()
{
int t;
t=1;
while(t--) {
Solved();
}
return 0;
}