NOIP 模拟赛(10.10):植物收集,美丽子区间,字符序列
植物收集
题面:
Dr. Wang是一位植物领域的专家。他要给他的学生们上一节课。课堂上需要展示一种植物。众所周知,植物的生长是有阶段的,本着严谨科学的态度,Dr. Wang 希望可以在课堂上给学生们展示该植物的每个生长阶段。
Dr. Wang要讲授的植物有n个阶段,现在他需要弄到该植物每种阶段各一株。他打听到了这种植物每个生长阶段的价格。但由于科研经费不足,有时候直接购买并不是一个好选择。所以他计划用上他的催熟科技。具体的,Dr. Wang可以进行如下两种操作:
- 以
的价格购买一株生长到第 个阶段的植物。 - 花费
的代价使用催熟科技,将所有已购买的植物生长阶段增加 。
若一株植物已经到了阶段
题解:
因为阶段
做法( pts):
观察发现,经过
做法( pts):
将
证明:
考虑Code:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
#define ll long long
#define inf 0x7f7f7f7f
int n,k,a[N<<1],st[N<<1][21],lg[N<<1],l,r,n1;
ll ans;
void init(){
memset(st,inf,sizeof(st));
for(int i=1;i<=n;i++)lg[i]=lg[i-1]+(1<<lg[i-1]==i);
for(int i=1;i<=n;i++)st[i][0]=a[i];
for(int j=1;(1<<j)<=n;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
}
ll check(int x){
ll res=0;
for(int i=x+1;i<=n1+x;i++){
int p=lg[x+1]-1;
res+=min(st[i-x][p],st[i-(1<<p)+1][p]);
}
return res;
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
// freopen("collect.in","r",stdin);
// freopen("collect.out","w",stdout);
cin>>n>>k;
l=0,r=n-1,n1=n;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i+n]=a[i];
ans+=a[i];
}
n<<=1;
init();
while(l<r){
int lmid=l+(r-l)/3,rmid=l+(r-l)/3*2;
if(lmid==rmid)break;
if(check(lmid)+1LL*k*lmid>check(rmid)+1LL*k*rmid)l=lmid;
else r=rmid;
}
for(int i=l;i<=r;i++){
ll t=check(i)+1LL*k*i;
ans=min(t,ans);
}
cout<<ans;
return 0;
}
美丽子区间
题面:
小
小
小
题解:
做法( pts):
st表预处理出所有区间最大值和最小值,暴力枚举所有长度大于等于
做法( pts):
考虑容斥,优美的定义是最大值和最小值都不出现在开头或结尾,这个条件难以限制,可以从容斥的角度考虑。优美区间的数量为:总区间数
之后我们会发现如果一个子区间的最大值出现在开头且最小值出现在结尾,这样的子区间被我们减去了两次,出现了减多了的情况。因此我们需要额外加回来一些区间,即加上:最大值在开头且最小值在结尾的子区间数量
求最大值在开头的子区间数量,可以使用单调栈,求每个数右边第一个大于该数的位置;如对于
之后求最大值在开头且最小值在结尾的子区间数量,继续考虑单调栈;根据上面的分析,
不难发现这样的
- 若使用模拟单调栈,注意边界和初始化。
- 注意长度为一的子区间会被重复减去
次,所以最后输出的结果为
Code:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define inf 0x7f7f7f7f
#define ll long long
const int N=2e5+5;
int n,a[N];
int lmin[N],lmax[N],rmin[N],rmax[N];
int w[N],s[N],p,l,r;
void work(){
memset(s,0,sizeof(s));
a[n+1]=inf;p=1;s[p]=1;
for(int i=2;i<=n+1;i++){
if(a[i]<a[s[p]])s[++p]=i;
else {
while(a[i]>a[s[p]]&&p)rmax[s[p--]]=i;
s[++p]=i;
}
}
memset(s,0,sizeof(s));
a[n+1]=0;p=1;s[p]=1;
for(int i=2;i<=n+1;i++){
if(a[i]>a[s[p]])s[++p]=i;
else {
while(a[i]<a[s[p]]&&p)rmin[s[p--]]=i;
s[++p]=i;
}
}
memset(s,0,sizeof(s));
a[0]=inf;p=1;s[p]=n;
for(int i=n-1;i>=0;i--){
if(a[i]<a[s[p]])s[++p]=i;
else {
while(a[i]>a[s[p]]&&p)lmax[s[p--]]=i;
s[++p]=i;
}
}
memset(s,0,sizeof(0));
a[0]=0;p=1,s[p]=n;
for(int i=n-1;i>=0;i--){
if(a[i]>a[s[p]])s[++p]=i;
else{
while(a[i]<a[s[p]]&&p)lmin[s[p--]]=i;
s[++p]=i;
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
//freopen("interval.in","r",stdin);
//freopen("interval.out","w",stdout);
cin>>n;
ll ans=1LL*n*(n+1)/2;
for(int i=1;i<=n;i++)cin>>a[i];
work();
for(int i=1;i<=n;i++)ans-=rmin[i]+rmax[i]-lmin[i]-lmax[i];
memset(s,0,sizeof(s));
p=1,s[p]=n;
for(int i=n-1;i>0;i--){
if(a[i]>a[s[p]]){
s[++p]=i;
l=1,r=p;
while(l<r){
int mid=(l+r)>>1;
if(s[mid]>rmax[i])l=mid+1;
else r=mid;
}
ans+=p-r;
}else{
while(a[i]<a[s[p]]&&p)--p;
s[++p]=i;l=1,r=p;
while(l<r){
int mid=(l+r)>>1;
if(s[mid]>rmax[i])l=mid+1;
else r=mid;
}
ans+=p-r;
}
}
memset(s,0,sizeof(s));
p=1,s[p]=1;
for(int i=2;i<=n;i++){
if(a[i]>a[s[p]]){
s[++p]=i;
l=1,r=p;
while(l<r){
int mid=(l+r)>>1;
if(s[mid]<lmax[i])l=mid+1;
else r=mid;
}
ans+=p-r;
}else{
while(a[i]<a[s[p]])p--;
s[++p]=i;l=1,r=p;
while(l<r){
int mid=(l+r)>>1;
if(s[mid]<lmax[i])l=mid+1;
else r=mid;
}
ans+=p-r;
}
}
cout<<ans+3*n;
return 0;
}
字符序列
题面:
小
小
不难发现这个函数的作用就是在
小
小
题解:
做法( pts)
考虑dp,设
做法:( pts)
解法一中的方程难以优化,考虑重构dp方式,设
观察发现所得字符串对称,设
Code:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int n;string s;
struct sqr{
long long a[30][30];
void init(){
for(int i=0;i<=26;i++)a[i][i]=1;
}
sqr(){
memset(a,0,sizeof(a));
}
sqr operator*(const sqr &x)const{
sqr z;
for(int k=0;k<=26;k++){
for(int i=0;i<=26;i++){
for(int j=0;j<=26;j++){
z.a[i][j]=(z.a[i][j]+a[i][k]*x.a[k][j])%mod;
}
}
}
return z;
}
}res;
sqr insert(int x){
sqr f;
f.init();
for(int i=0;i<=26;i++)f.a[i][x]=1;
return f;
}
int main(){
//freopen("subseq.in","r",stdin);
//freopen("subseq.out","w",stdout);
cin>>n;
cin>>s;
s=' '+s;
long long ans=0;
res.init();
for(int i=n;i>=1;i--)res=res*insert(s[i]-'a')*res;
for(int i=0;i<26;i++)ans=(ans+res.a[26][i])%mod;
cout<<ans;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架