注意到程序的运行是连续的,所以假设序列最左端左边还有一个
>
>
(且不能删除),那么一个区间子程序运行的过程一定时整个程序运行过程的一个子段。于是只需要用链表模拟一边总程序,再求出子段的开始和结束时间,直接用前缀和减一下即可。
设表示指针第一次从
i−1
i
−
1
移动到
i
i
的时间,表示指针第一次从
i
i
移动到的时间,那么
[l,r]
[
l
,
r
]
对应的子段开始时间就是
fl
f
l
,结束时间就是
min(fr+1−1,gl)
min
(
f
r
+
1
−
1
,
g
l
)
。
f
f
直接记录即可,但考虑到某些位置还没有从右向左走过就被删除了,会导致某些记录不到,我们只要删除
i
i
的时候先标记下,等记录过之后再真正删去即可。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200010
using namespace std;
int n,q,b[N],ans[N*10][10],to[2][N],f[N],g[N],tim;
char cs[N];
bool ex[N];
int read()
{
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*f;
}
int main()
{
n=read();q=read();
scanf("%s",cs+1);
for(int i=1;i<=n;i++)
if(cs[i]=='<') b[i]=-2;
else if(cs[i]=='>') b[i]=-1;
else b[i]=cs[i]-'0';
b[0]=-1;
memset(ans,0,sizeof(ans));
memset(f,0x3f,sizeof(f));
memset(g,0x3f,sizeof(g));
int v=1,pos=1,lst=1;f[1]=tim=1;
for(int i=0;i<=n;i++)
ex[i]=1,to[0][i]=i-1,to[1][i]=i+1;
to[1][0]=1;
while(pos<=n)
{
if(ex[pos])
{
if(b[pos]>=0)
{
ans[tim][b[pos]]++;
if(b[pos]>0) b[pos]--;else ex[pos]=0;
}
if(b[pos]<0) v=b[pos]+2;
}
if(!v)
{
g[pos]=min(g[pos],tim);
if(!ex[pos]) to[1][to[0][pos]]=to[1][pos],to[0][to[1][pos]]=to[0][pos];
}
pos=to[v][pos];
if(!ex[pos]) continue;
++tim;
memcpy(ans[tim],ans[tim-1],sizeof(ans[tim]));
if(v) f[pos]=min(f[pos],tim);
if(lst&&b[lst]<0&&b[pos]<0) ex[lst]=0;
lst=pos;
}
f[n+1]=tim;
while(q--)
{
int l=read(),r=read();
for(int i=0;i<=9;i++)
printf("%d ",ans[min(f[r+1]-1,g[l])][i]-ans[f[l]-1][i]);
puts("");
}
return 0;
}