植物收集

题面:

Dr. Wang是一位植物领域的专家。他要给他的学生们上一节课。课堂上需要展示一种植物。众所周知,植物的生长是有阶段的,本着严谨科学的态度,Dr. Wang 希望可以在课堂上给学生们展示该植物的每个生长阶段。
Dr. Wang要讲授的植物有n个阶段,现在他需要弄到该植物每种阶段各一株。他打听到了这种植物每个生长阶段的价格。但由于科研经费不足,有时候直接购买并不是一个好选择。所以他计划用上他的催熟科技。具体的,Dr. Wang可以进行如下两种操作:

  • 以$a_i$的价格购买一株生长到第$i$个阶段的植物。
  • 花费$k$的代价使用催熟科技,将所有已购买的植物生长阶段增加$1$。

若一株植物已经到了阶段$n$,则返回阶段$1$可以理解为成熟到只剩种子,然后被重新种下去了)。现在Dr. Wang想让你帮忙求出,他最少需要花费多少代价,可以收集到植物的每个生长阶段。

题解:

因为阶段$n$的植物会返回阶段$1$,所以破环为链,下文所有$n$均视为$2n$

\(n^2\)做法(\(80\)pts):

观察发现,经过$k$次操作二后,阶段$i$植物的价值可看作:$$min_{j=i-k}^i a_j$$,可使用st表维护区间最小值,然后枚举操作二的次数,取$min$即可。

\(n \log n\)做法(\(100\)pts):

将$n^2$做法中枚举的最终代价输出,注意到代价是一个关于$k$的单谷函数,三分即可。

证明:

考虑$m \rightarrow m+1$时,每轮购买植物能节省的钱是单调不增的,但每轮使用科技都是增加$k$,所以二者相加必定会存在峰值,且两个方向均单调不增。

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;
}

美丽子区间

题面:

小\(Z\)喜欢区间。

小\(Z\)定义一个区间是美丽的,当且仅当这个区间的最大值或最小值不出现在区间的开头和结尾。如\([3,2,4,1]\)不是一个美丽的区间,因为最小值\(1\)出现在了结尾;而\([2,4,1,3]\)是一个美丽的区间,因为\(1,4\)没有出现在开头或结尾。

小\(Z\)有一个排列,现在需要求出这个序列中有多少个子区间是美丽的。

题解:

\(n^2\)做法(\(40\)pts):

st表预处理出所有区间最大值和最小值,暴力枚举所有长度大于等于$4$的子区间判断是否合法

\(n \log n\)做法(\(100\)pts):

考虑容斥,优美的定义是最大值和最小值都不出现在开头或结尾,这个条件难以限制,可以从容斥的角度考虑。优美区间的数量为:总区间数$-$最大值在开头的子区间数量$-$最小值在开头的子区间数量$-$最大值在结尾的子区间数量$-$最小值在结尾的子区间数量。

之后我们会发现如果一个子区间的最大值出现在开头且最小值出现在结尾,这样的子区间被我们减去了两次,出现了减多了的情况。因此我们需要额外加回来一些区间,即加上:最大值在开头且最小值在结尾的子区间数量$+$最大值在开头且最小值在结尾的子区间数量。

求最大值在开头的子区间数量,可以使用单调栈,求每个数右边第一个大于该数的位置;如对于$p_i$,其右边第一个大于他的位置在$r_i$,那么$\forall j \in [i,r_i)$,子区间$[i,j]$都是最大值在开头的子区间。最小值在开头的子区间等同理可求。

之后求最大值在开头且最小值在结尾的子区间数量,继续考虑单调栈;根据上面的分析,$\forall j \in [i,r_i)$,子区间$[i, j]$都是最大值在开头的子区间,那需要求有多个$j$满足$p_j$是$[i, j]$的最小值。

不难发现这样的$j$,可以被一个从后往前加入元素的单调栈维护出来。只需要再单调栈中二分有多少个$j$满足$j < r_i$即可

  • 若使用模拟单调栈,注意边界和初始化。
  • 注意长度为一的子区间会被重复减去$4$次,所以最后输出的结果为$ans+3*n$
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;
}

字符序列

题面:

小\(Z\)喜欢字符串。

小\(Z\)定义了一个奇妙的函数\(f(c, s)\),对于一个长度为\(n\)的字符串\(f(c, s) = cs_1cs_2c . . . cs_nc\),其中 c 是一个小写英文字母。

不难发现这个函数的作用就是在\(s\)的每一个空位插入一个小写英文字母,如\(f(c, aba) =cacbcac\)。

小\(Z\)通过这个函数构造了使用了\(n\)个小写字母\(c_1, c_2 . . . c_n\)构造\(n\)个字符串\(s_1, s_2, . . . s_n\),其中 \(s_i = f(c_i, s_i−1)\),其中\(s_0\)空串。

小\(Z\)也非常喜欢子序列,现在他想知道\(s_n\)中有多少个本质不同的非空子序列。

题解:

$\vert \sum \vert$表示字符集大小

\(2^n\)做法(\(50\)pts)

考虑dp,设$f_i$为前i个字符组成的非空子序列个数,$last_{i,c}$为i前面的上个字符c的位置,容易得出状态转移方程(c为当前字符,$last_c$数组为滚动数组):
$$\begin{cases}
f_i=f_{i-1}*2+1&last_c=0\\
f_i=f_{i-1}*2-f_{last_c-1}&last_c \neq 0\\
\end{cases}$$
将字符串展开,dp计数即可。

\(n{\vert \sum \vert}^3\)做法:(\(100\)pts)

解法一中的方程难以优化,考虑重构dp方式,设$f_{i,c}$为前i个字符组成的以字符c结尾的非空子序列数量,由此推出转移方程($s_i$为位置i的字符):
$$
\begin{cases}
f_{i,c} = f_{i-1,c} & c \neq s_i\\
f_{i,c} = \sum_{j=1}^{\vert \sum \vert} f_{i-1,j}+1& c=s_i\\
\end{cases}
$$
该dp复杂度为$O(2^n\vert\sum\vert)$,考虑优化,发现该dp可写成矩阵形式,对角线变为一,转移矩阵中对应字母所在的列变为一:
$$
\begin{bmatrix}0&0&\cdots&0&0&1\end{bmatrix}\times\begin{bmatrix}1&0&1&0&\cdots&0\\0&1&1&0&\cdots&0\\0&0&1&0&\ddots&0\\0&0&1&1&\ddots&0\\\vdots&\vdots&\vdots&\vdots&\ddots&\vdots\\0&0&1&0&\cdots&1\\\end{bmatrix}\times\begin{bmatrix}\cdots&\cdots&\cdots&\cdots&\cdots&\cdots\\\vdots&\vdots&\vdots&\vdots&\vdots&\vdots\\\ddots&\ddots&\ddots&\ddots&\ddots&\ddots\\\vdots&\vdots&\vdots&\vdots&\vdots&\vdots\\\cdots&\cdots&\cdots&\cdots&\cdots&\cdots\\\end{bmatrix}\times\cdots
$$

观察发现所得字符串对称,设$T_i$为从操作i执行到操作n获得的字符串,发现$T_i=T_{i+1}+s_i+T_{i+1}$,由此从n至1逆序合并矩阵,并输出矩阵最后一行的和即可

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;
}

结束喽(T4不会)

NOIP 模拟赛(10.10):植物收集,美丽子区间,字符序列的更多相关文章

  1. NOIP模拟赛-2018.10.22

    模拟赛 今天第一节课是历史,当然是不可能上的,一来到机房发现今天高二考试... 老师说以后可能还要给高一考...那还不如现在跟着做好了,毕竟在学长学姐中垫底显得没那么丢人 这套题风格挺奇怪的...为什 ...

  2. NOIP模拟赛 17.10.10

    初次见面(firstmeet)[题目背景]雾之湖边,静得可怕.露米娅出神凝望.黑白连衣裙,像极了绽放的墨黑和洁白的莲.身边的雾之湖,倒映着血色天空.酒红的双眸,映照一切.低声浅笑,双臂伸直,她悄无声息 ...

  3. noip模拟赛(10.4) 序列(sequence)

    序列(sequence) [题目描述] 给定一个1~n的排列x,每次你可以将x1~xi翻转.你需要求出将序列变为升序的最小操作次数.有多组数据. [输入数据] 第一行一个整数t表示数据组数. 每组数据 ...

  4. noip模拟赛(10.4) 背包(pack)

    [题目描述] 蛤布斯有n种商品,第i种物品的价格为ai,价值为bi.有m个人来向蛤布斯购买商品,每个人每种物品只能购买一个.第j个人有cj的钱,他会不停选择一个能买得起的价格最高的商品买走(如果有多个 ...

  5. noip模拟赛(10.4) 字典序(dictionary)

    [题目描述] 你需要构造一个1~n的排列,使得它满足m个条件,每个条件形如(ai,bi),表示ai必须在bi前面.在此基础上,你需要使它的字典序最小. [输入数据] 第一行两个正整数n,m.接下来m行 ...

  6. 水(NOIP模拟赛Round #10)

    题目描述: 小Z有一个长度为的数列.他有次令人窒息的操作,每次操作可以使某个数字或.他当然是希望这些数字的乘积尽量小了.为了简化题目,你只需输出操作完成后的数列即可. ———————————————— ...

  7. 题(NOIP模拟赛Round #10)

    题目描述: 有一张的地图,其中的地方是墙,的地方是路.有两种操作: 给出个地点,询问这个地点中活动空间最大的编号.若询问的位置是墙,则活动空间为:否则活动空间为询问地点通过四联通能到达的点的个数.如果 ...

  8. 大(NOIP模拟赛Round #10)

    题目描述: 小Z有个n个点的高清大图,每个点有且只有一条单向边的出边.现在你可以翻转其中的一些边,使他从任何一个点都不能通过一些道路走回这个点.为了方便,你只需输出方案数对取模即可.当在两个方案中有任 ...

  9. 10.17 NOIP模拟赛

    目录 2018.10.17 NOIP模拟赛 A 咒语curse B 神光light(二分 DP) C 迷宫maze(次短路) 考试代码 B 2018.10.17 NOIP模拟赛 时间:1h15min( ...

  10. 10.16 NOIP模拟赛

    目录 2018.10.16 NOIP模拟赛 A 购物shop B 期望exp(DP 期望 按位计算) C 魔法迷宫maze(状压 暴力) 考试代码 C 2018.10.16 NOIP模拟赛 时间:2h ...

随机推荐

  1. QT5笔记: 28. SplashWindow 没听懂,无内容

    没有说明这个SplashWindow咋用 大概小人愚笨 this->setWindowFlag(Qt::SplashScreen);莫非是这个?

  2. mybatis - [04] mapper文件详解

      Mybatis的Mapper文件(通常是以.xml为扩展名的文件)主要用于定义SQL语句和它们与Java接口方法之间的映射关系.以下是Mapper文件中一些常用的配置元素和属性. 一.mapper ...

  3. Python基础--python数据结构(字符串、列表和元组)

    前言 !!!注意:本系列所写的文章全部是学习笔记,来自于观看视频的笔记记录,防止丢失.观看的视频笔记来自于:哔哩哔哩武沛齐老师的视频:2022 Python的web开发(完整版) 入门全套教程,零基础 ...

  4. 内网环境部署Deepseek+Dify,构建企业私有化AI应用

    0.简介 公司为生产安全和保密,内部的服务器不可连接外部网络,为了可以在内网环境下部署,采用的方案为ollama(Docker)+Dify(Docker Compose),方便内网环境下迁移和备份,下 ...

  5. ABAQUS-循环对称条件的详解

    概括 anlysis of model that exhibit cyclic symmetry 循环对称分析技术用于Standard求解器. makes it possible to analyze ...

  6. Windows 提权-内核利用_1

    本文通过 Google 翻译 Kernel Exploits Part 1 – Windows Privilege Escalation 这篇文章所产生,本人仅是对机器翻译中部分表达别扭的字词进行了校 ...

  7. 【Azure Fabric Service】演示使用PowerShell命令部署SF应用程序(.NET)

    问题描述 在中国区微软云Azure上使用Service Fabrics服务,本地通过Visual Studio 2022的发布.NET应用,发现无法发布! 在搜寻官方文档之后,可以通过PowerShe ...

  8. 使用Docker部署服务

    一.Docker概念 1.操作系统层面的虚拟化技术 2.隔离的进程独立于宿主和其它的隔离的进程 - 容器 3.GO语言开发 4.特点:高效的利用系统资源:快速的启动时间:一致的运行环境:持续交付和部署 ...

  9. Delphi Inputbox 输入时显示‘*’号

    unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms ...

  10. [源码系列:手写spring] IOC第九节:应用上下文ApplicationContext

    内容介绍 在Spring中应用上下文ApplicationContext是相较于BeanFacotry更为先进的IOC容器,BeanFacotry是Spring实现IOC最基础最核心的接口,使得Spr ...