A. 小红的正整数自增
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=1e5+10;
int n,m,a[N];
void solve()
{
cin>>n;
if(n%10==0) cout<<0<<endl;//末尾为0,直接输出
else cout<<10-n%10<<endl;//10-末尾
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
int T=1;
// cin>>T;
while(T--)
{
solve();
}
return 0;
}
B. 小红的抛弃后缀
由性质(a+b)%p=(a%p+b%p)%p
每次加上一位,然后取模即为结果
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=1e5+10;
int n,m,a[N];
string s;
void solve()
{
cin>>s;
n=s.size();
int res=0,ans=0;
for(int i=0;i<n;i++)
{
m=s[i]-'0';//字符->整数
res=(res+m)%9;//每次取模都会让结果变成1位数,无需乘10
if(res==0) ans++;//为0,答案加1
}
cout<<ans;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
int T=1;
// cin>>T;
while(T--)
{
solve();
}
return 0;
}
C. 小红的字符串构造
可以观察到:
abab,存在一个回文串
abcabcabc不存在回文串
所以只需要三个字母依次循环输出,回文数为0
再观察:
aa,一个回文串
abba,两个回文串
abccba,三个回文串
abcaacba,四个回文串
所以要想输出k个回文串,2*k个字母足以,题目范围(k<=n/2),满足
所以每次正着输出k个,再倒着输出k个,然后再用另外三个字母补到n就行了
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=1e5+10;
int n,k,a[N];
char c[3]={'a','b','c'},d[3]={'x','y','z'};
void solve()
{
cin>>n>>k;
for(int i=0;i<k;i++) cout<<c[i%3];//正
for(int i=k-1;i>=0;i--) cout<<c[i%3];//倒
for(int i=k*2;i<n;i++) cout<<d[i%3];//补
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
int T=1;
// cin>>T;
while(T--)
{
solve();
}
return 0;
}
D. 小红的平滑值插值
每次找相邻两个数的差就行了
有一点,k=2时,差为3时,1个就行,即3/2
但是,差为4时,也是1个就行了,所以这时候先减1就行,(4-1)/2
最后一种,就是最大差值小于k,我们只需要在a,b间加上一个数就行,取min(a,b)+k
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=1e5+10;
int n,k,a[N],b[N];
void solve()
{
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
int mx=0;
for(int i=1;i<n;i++) b[i]=abs(a[i+1]-a[i]),mx=max(mx,b[i]);
int ans=0; //记录每两位差值,最大差值,mx<k,就可以直接输出1了
for(int i=1;i<n;i++)
{
if(b[i]>k)
{
if(b[i]%k==0) b[i]--;//整除,就让它不整除
ans+=b[i]/k;//每次加上
}
}
if(mx<k) ans++;//+1
cout<<ans;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
int T=1;
// cin>>T;
while(T--)
{
solve();
}
return 0;
}
E. 小苯的等比数列
注意他们之间可能有多组重复,如 1 2 1 1 2这种
所以需要提前记录个数,取最大值
暴力即可,经过计算,公比>2,累乘>2e5,需要乘的没几个
从2~n,总计4e5左右(记不清了),所以再剪枝就行了
如果当前个数为3,公比为2,当前数为x
若x*23>mx(数组中的最大值),那么从x开始,公比为2,就没必要再算了,再算也不可能超过3
这样将会少算非常非常多次,时间大概率是够了
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=2e5+10;
int n,m,a[N],mp[N];
void solve()
{
int ans=1;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]]++,ans=max(ans,mp[a[i]]);
sort(a+1,a+n+1);//排序 //提前记录个数,取最大值,此时公比为1
for(int i=2;i<=a[n];i++)//从2开始,枚举公比,不可能超过a[n]
{
for(int j=1;j<=n;j++)//枚举n个元素
{
if(pow(i,ans)*a[j]>a[n]) break;//最重要的一步,剪枝,如果害怕爆范围可以log求
int res=a[j],cnt=1;//对a[j]进行倍增
while(res*i<=a[n]&&mp[res*i])//在范围内,且存在
{
res*=i;
cnt++;
}
ans=max(ans,cnt);//取max
}
}
cout<<ans;//输出0^0
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
int T=1;
// cin>>T;
while(T--)
{
solve();
}
return 0;
}
F. 小苯的回文询问
回文长度>2,即存在(a,b,a)这种情况即可
因为是子序列,随便选三个,有两个相同,两个之间有一个就行
我们可以先排个序,这样相邻两个就是相同的(这里有点不好说,懂我意思就行),
再存一下下标,判断相同的两个下标,是否相差>2即可
按顺序排序,第一个找到的就是最小的
直接令 m [ i ] = id,即可
至于不存在相同,或者相同但跨度过大
如 3 1 2 1 1 4 1 2
对4,直接取>n的数就行
对3,取它后面离他最近的一对就行
对第一个2,本来跨度为6,但是,有两个1,在两个2中间,且跨度为4,所以,
取(下标,从1开始)[3,7],而不是[3,8]
对于询问的区间[l,r],我们只需要判断 m [ l ] 是否小于r即可
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=1e5+10;
#define x first //提前定义x为first,y为second
#define y second
int n,q,a[N],b[N];//b[i]存i对应的最小右边界
void solve()
{
vector<pair<int,int>>v;//pair,相当于结构体,排序时,默认先按第一个排,再按第二个元素排
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i],v.push_back({a[i],i});
sort(v.begin(),v.end()); //先按元素大小,再按下标
for(int i=0;i<n;i++)
{
for(int j=1;j<=2;j++)//排好序的元素,相邻两个的下标的差可能为1,也可能>1
{ //但是不相邻的两个一定>1,所以这里比较[i,i+j]
if(i+j<n&&v[i].x==v[i+j].x&&v[i+j].y-v[i].y>=2)
{//在范围内,两个元素相等,下标差>=2
b[v[i].y]=v[i+j].y;//第一个就是最小的,直接break
break;
}
}
}
int mn=n+1;//取一个大于n的值
for(int i=n;i>=1;i--)//☆倒着遍历
{
if(b[i])//b[i]有对应值,就先每次取min,以便没有对应值的元素取值,
mn=min(mn,b[i]); //同时也是更新每个元素能对应的最小值
b[i]=mn;//更新 //即上面举例中两个2的更新
}
while(q--)
{
int l,r;
cin>>l>>r;
if(b[l]<=r)//l对应的最小右边界<=r,YES,否则,NO
{
cout<<"YES\n";
}
else cout<<"NO\n";
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
int T=1;
// cin>>T;
while(T--)
{
solve();
}
return 0;
}
G. 小红的区间删除
树状数组+双指针
我们先算出整个区间[1,n]的逆序对数量
因为数据范围是1e6,所以每次加入 a [ i ],然后加上 a [ i ] 前面有几个比它大的数的个数就行
树状数组每次询问都是前x个的和,所以算逆序对总数 (res),每次加从最后面减去当前即可
(1,n)-(1,x)=(x,n),注意这里n并不是个数,而是最大值
当然,如果数据范围很大,比如1e9,就需要离散化了
每次去掉一个数时,比如 5 4 3 2 1,去掉 [2,2] ,当前逆序对总数即为
res = res - [1,2) 中比 4 大的个数 - (2,5] 中比 4 小的个数,
去掉 [3,3],res = res - [1,3)中比 3 大的个数 - (3,5] 中比 3 小的个数
假使去掉一个区间 [2,3],比如 5 4 3 2 1,此时 res = 10
先去掉 [2,2],res = 10 - (1 + 3)= 6,再去掉 [3,3],注意,此时已经去掉第二个元素,
所以此时,res = 6 -(1 + 2)= 3
设区间 [ l,r ]
我们每次增加右端点 r,每次 r 都会有一个对应的 l,使得 res - [ l,r ] 的逆序对个数最接近 k,
每次加上 r - l + 1,即加入区间 [ l,r ],[ l + 1,r ],… ,[ r,r ],总个数为 r - l +1,
可以想到,对于每一个 r,都加入了一定的满足条件的区间,这样总和起来的区间将是不重不漏的
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=1e6+10;
int n,k,a[N],L[N],R[N];//两个树状数组,L存这个数左边比它大的个数,R存右边比它小的个数
int lowbit(int x)
{
return x & -x;
}
void add(int x,int c,int tr[])
{
for(int i=x;i<N;i+=lowbit(i)) tr[i]+=c;
}
int sum(int x,int tr[])
{
int res=0;
for(int i=x;i>=1;i-=lowbit(i)) res+=tr[i];
return res;
}
void solve()
{
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
int res=0,cnt=0,mx=N-1;//a中元素最大值不超过mx
for(int i=1;i<=n;i++)
{
add(a[i],1,R);//a[i]的个数加1
cnt+=sum(mx,R)-sum(a[i],R);//每次加入 (a[i],mx]的总和,即大于a[i]的个数
}
if(cnt>=k) res++;//如果不删除满足,答案加1
int l=1,r=1;
while(r<=n)//r从1~n
{
add(a[r],-1,R);//每次删除第r个数,此时-1即删除
cnt-=sum(mx,L)-sum(a[r],L)+sum(a[r]-1,R);//减去左边比a[r]大的,右边的a[r]小的
while(cnt<k&&l<=r)//cnt<k,该走左指针了
{
add(a[l],1,L);//加入第l个数
cnt+=sum(mx,L)-sum(a[l],L)+sum(a[l]-1,R);//加上第l个数所能带来的增益
l++; //左边大于a[l]的个数,右边小于a[l]的个数
}
res+=r-l+1;//加入区间长度,即个数
r++;
}
cout<<res;//输出
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
int T=1;
// cin>>T;
while(T--)
{
solve();
}
return 0;
}