JZOJ 5623 program

program

Description

对于一个仅有<、>、0~9组成字符串,执行一种操作,一开始指针在开头位置,指针移动方向默认为向右移,当遇到<或>,指针移动方向就要对应的修改成向左或向右。
当连续遇到两个非数字字符时,便要删掉前一个<或>。
若遇到了一个数字,便将此数打印出来,并将其数字-1,当发现一个数字在打印之前为0,打印后便将其删掉。
若指针移到了字符串外,结束整个过程。

现在给出一个长度为 n 的仅有<、>、0~9组成字符串,给出q个询问,每个询问由二元组( l ,r)组成,表示询问将区间[ l ,r]当成一个独立的字符串执行操作时0~9每个数字被打印的次数。

Data Constraint

1 <=n, q <=105

Solution

在字符串的前面加入足够的>,然后从开头对整个串执行操作,记下每个时刻下每个数字的打印次数。
可以发现一个区间[ l ,r]0~9每个数字被打印的次数对应上对原串操作时一段连续的时刻内每个数字的打印次数。
那显然这段时刻的开始点就是指针第一次到 l 位置的时间,结束点便是第一次到达r+ 1 的时间 和 是第一次到l前面的时刻的较小值,这个在对原串进行操作时便可以预处理出来,同时记录下每个数字出现次数关于时间的前缀和,询问时就能做到 O(1) 查询了。

时间复杂度 O(10n+q)

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

#define fo(i,j,l) for(int i=j;i<=l;++i)
#define fd(i,j,l) for(int i=j;i>=l;--i)

using namespace std;
typedef long long ll;
const ll M=31e5,K=1e9;

int n,q,x,y;

int qz[M][10];
char s[M];
int next[M],last[M],ne[M],la[M];
int f[M],g[M];

inline int min(int a,int b)
{return a<b?a:b;}

inline void erase(int p)
{ne[la[p]]=ne[p]; la[ne[p]]=la[p];}

int main()
{
    cin>>n>>q;
    scanf("%s",s+2*n+1);
    fo(i,1,n*2)s[i]='>';
    int dir=1,dq=n*2,now=n*2; f[now]=0;
    fo(i,1,3*n+1)next[i]=ne[i]=i+1,last[i]=la[i]=i-1;
    next[0]=ne[0]=1;
    fo(i,0,3*n+1)f[i]=g[i]=K;
    int k=1;
    for(;now>0&&now<=(n*3);++k){
        f[now]=min(f[now],k);
        fo(i,0,9)qz[k][i]=qz[k-1][i];
        if(s[now]>='0'&&s[now]<='9')++qz[k][s[now]-48];
        else dir=s[now]=='<'?0:1;
        int wz=dir==0?last[now]:next[now];
        if(wz<=0||wz>n*11){
            now=wz; continue;
        }
        if(s[now]>='0'&&s[now]<='9'){
            s[now]=='0'?(last[next[now]]=last[now],next[last[now]]=next[now]):s[now]=s[now]-1;
            if(dir==0){
                g[now]=min(g[now],k);
                for(;dq>wz;dq=la[dq])g[dq]=k,erase(dq);
            }
        }else{
            if(s[wz]=='<'||s[wz]=='>'){
                (last[next[now]]=last[now],next[last[now]]=next[now]);
            }
            if(dir==0){
                g[now]=min(g[now],k);
                for(;dq>wz;dq=la[dq])g[dq]=k,erase(dq);
            }
        }
        now=wz;
        for(;ne[dq]<=now;)dq=ne[dq];
        for(;dq>now;)dq=la[dq];
    }
    if(now==(n*11)+1)f[now]=k;
    fo(i,1,q){
        scanf("%d%d",&x,&y);
        int qm=f[x+2*n]-1,hm=min(f[y+2*n+1]-1,g[x+2*n]);
        fo(l,0,9)printf("%d ",qz[hm][l]-qz[qm][l]);
        puts("");
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值