小美的密码
思路:长度小于正确长度的肯定是要尝试的,假设为x,那么最优的就是下一步登陆成功,也就是x+1,最坏的就是对于所有长度等于正确答案的,最后一次才尝试到正确答案,所以答案就是x+和密码长度相同的字符串个数,需要注意重复的字符串不能计算两次
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
string s,right_s;
map<string,bool> mp;
int main()
{
int n;
cin>>n;
cin>>right_s;//存储正确密码
int right_len=right_s.size();
int mx=0,mn=0;
for(int i=1;i<=n;i++)
{
cin>>s;
if(mp[s]) continue;//已经出现过就跳过
mp[s]=true;//标记为已经出现过
int len=s.size();
if(len<right_len)
{
mx++;
mn++;
}
else if(len==right_len)
mx++;
}
printf("%d %d",mn+1,mx);
return 0;
}
小美的数组删除

思路:容易发现,操作2最多执行一次(因为执行一次操作2数组就已经被删除),而且一定是最终执行,但在执行操作2前可能执行操作1,于是我们可以直接枚举在执行操作2前一共执行了多少次操作1,整体复杂度就是O(n)
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int a[N];
int main()
{
int T;
cin>>T;
while(T--)
{
long long n,k,x;
scanf("%lld%lld%lld",&n,&k,&x);
map<int,int> mp;//mp[x]记录数字出现次数
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
mp[a[i]]++;
}
int mex=0;//记录mex
for(int i=0;i<=n;i++)
{
if(mp[i]==0)
{
mex=i;
break;
}
}
long long ans=k*mex;//对应于直接执行操作2的情况
for(int i=1;i<=n;i++)//枚举在操作2前执行了i次操作1
{
mp[a[i]]--;
if(mp[a[i]]==0&&a[i]<mex)//更新mex
mex=a[i];
ans=min(ans,1ll*i*x+k*mex);
}
printf("%lld\n",ans);
}
return 0;
}
小美的彩带

思路:容易发现,当剪下来长度大于等于n时,答案就是彩带上的所有颜色,当长度小于n时,我们可以看作两种情况:
情况1:[L,R]在一个彩带上

情况2:[L,R]横跨两个彩带

这里有一个技巧:其实对于循环的彩带而言也就类似于环,我们可以通过把一条彩带复制一倍来等价替换。这里就等价于彩带长度是2*n,前一半和后一半是完全相同的。由于这两种情况对应的长度都是小于n的,我们现在就转化为求解从某一个点开始长度为len的区间中的不同颜色个数。于是自然而然就能想到莫队来做(莫队就是一种利用分块思想来优化暴力求解的方法,不懂的同学可以移步我之前的讲解博客:莫队(离线处理区间询问)_莫队问题-CSDN博客)。需要注意的就是莫队的复杂度是n*sqrt(n),这道题的数据是2*10的五次方,还是比较极限的。由于彩带的颜色有10的9次方种,理论上简单的方法是用map存储颜色,但是map时间耗费比较严重,因此我们需要用数组直接存储,所以需要用到离散化,这里特别需要注意一下。其他就没什么了,下面是代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
struct node{
int l,r,id;
}p[N];
int a[N];
int mp[N];//记录每一种颜色出现的次数
int ans[N];//ans[pl]记录第pl个区间的答案
int cnt_color;//记录实时区间颜色数量
void add(int x)
{
if(mp[a[x]]==0) cnt_color++;//实时区间新加入一个颜色
mp[a[x]]++;
}
void sub(int x)
{
mp[a[x]]--;
if(mp[a[x]]==0) cnt_color--;//实时区间新减去一个颜色
}
int pn;
bool cmp(node a,node b)
{
//按照询问左边界所在的块为第一优先级,右边界的大小为第二优先级进行排序
if(a.l/pn!=b.l/pn) return a.l<b.l;
return a.r<b.r;
}
vector<int> alls;//用于离散化
int find(int x)
{
return lower_bound(alls.begin(),alls.end(),x)-alls.begin()+1;
}
int main()
{
int n,q;
cin>>n>>q;
int cnt=0;//记录总颜色数
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
alls.push_back(a[i]);
}
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
cnt=alls.size();
for(int i=1;i<=n;i++)
{
a[i]=find(a[i]);
a[i+n]=a[i];
}
char op[2];
int len,m=0;//m记录要排序的块数
pn=sqrt(2*n);
int l=1,r=2*n;
for(int i=1;i<=q;i++)
{
scanf("%s%d",op,&len);
if(len>=n)
{
ans[i]=cnt;
len%=n;
}
if(op[0]=='L')
{
p[++m].l=l;
p[m].r=l+len-1;
p[m].id=i;
l+=len;
while(l>n) l-=n;
}
else
{
p[++m].l=r-len+1;
p[m].r=r;
p[m].id=i;
r-=len;
while(r<=n) r+=n;
}
}
sort(p+1,p+m+1,cmp);
int mo_l=0,mo_r=0;
for(int i=1;i<=m;i++)
{
while(mo_l<p[i].l) sub(mo_l++);
while(mo_l>p[i].l) add(--mo_l);
while(mo_r<p[i].r) add(++mo_r);
while(mo_r>p[i].r) sub(mo_r--);
if(ans[p[i].id]==0)//还没有填充值
ans[p[i].id]=cnt_color;
}
for(int i=1;i<=q;i++)
printf("%d\n",ans[i]);
return 0;
}