【gmoj】【树状数组】【st表】No Time to Dry
题目
解题思路
qwq,第一题放紫题,人麻了
看到区间想到树状数组 or 线段树
手推一下会发现区间[l…r]且s[l]==s[r]时,如果中间有数小于s[l],那么s[r]必须要靠操作一次来涂色
那么先预处理出一个bf数组维护当前颜色上一次出现的位置
如果当前和上一次之间没有比它们小的,说明涂色可以传递,那就在上一次出现的位置+1加一次操作如果上一次位置不在区间内,是要加1的,如果在区间内不影响结果
否则要单独涂一次,直接在1加一次操作
最后答案是ans[i]=get(r,l)-get(l,l)
get(i,j)表示进行到第i块木板时前j块模板用到的操作次数
代码
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
struct lzf{
int l,r,z,id;
}f[400010];
int n,q,l,r,t,j=1,d[200010],mi[200010][30],bf[200010],v[200010],ans[200010];
bool cmp(lzf l,lzf y)
{
return l.r<y.r;
}
int query(int l,int r) //st表,找区间最小
{
int k=log2(r-l+1);
return min(mi[l][k],mi[r-(1<<k)+1][k]);
}
void add(int x,int y) //区间加
{
for (;x<=n;x+=x & (-x))
d[x]+=y;
}
int get(int x) //区间求和
{
int sum=0;
for (;x;x-=x & (-x))
sum+=d[x];
return sum;
}
int main()
{
scanf("%d%d",&n,&q);
for (int i=1;i<=n;i++)
{
scanf("%d",&mi[i][0]);
bf[i]=v[mi[i][0]]; //存上一次出现的位置
v[mi[i][0]]=i;
}
for (int i=1;i<=q;i++)
{
scanf("%d%d",&l,&r);
if (l>1) f[++t]=(lzf){l,l-1,-1,i};
f[++t]=(lzf){l,r,1,i};
}
sort(f+1,f+t+1,cmp);
for (int j=1;j<=21;j++)
for (int i=1;i+(1<<j)-1<=n;i++)
mi[i][j]=min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);
for (int i=1;i<=n;i++)
{
int x=query(bf[i]+1,i);
if (x<mi[i][0]) add(1,1);
else add(bf[i]+1,1);
while (f[j].r==i&&j<=t)
{
ans[f[j].id]+=get(f[j].l)*f[j].z;
j++;
}
}
for (int i=1;i<=q;i++)
printf("%d\n",ans[i]);
return 0;
}