CF 238D Tape Programming

链表+模拟

VFK题解:应该能注意到这个编程语言有特殊性质即指针的移动是连续的,这意味着假设我们在开头处放置足够多的“>”,那么单独取一个区间出来执行时的程序一定是完整地执行整个代码的程序的一部分。于是我们可以做一个前缀和。f[i][d] 表示第i 个字符在准备第一次访问到的时候数字d 输出了多少次,g[i][d] 表示第i 个字符在指针准备从此处向左移走的时候数字d 输出了多少次。然后查询一段区间时,程序肯定从左端点开始执行,但有可能从左边或者右边出去。于是我们可以比较下左端点的f 和右端点的g 的时间,然后安心地使用前缀和相减得到结果。

说是这么说的,然而代码实现卡了我差不多一个下午。。。用链表模拟要注意如”00000<”的串,在查询[3,6]时是无法得到3处准备左移的时间的,因为在做第3位的时候3已经被删掉了。。。因此为了防止这种情况发生,在orz了汪队的代码之后,我发现更新左移最早时间只需要把它们都压到栈里,等到指针左移时暴力把下标在左移的区间内的栈内元素更新即可。。。(顺便吐槽一下,CF上很多代码时间复杂度是错的(?),只要用”>9>9>9>9>9…<9<9<9<9<9”这种就能卡掉,我该说啥好。。。)

话说这题真有意思呀,tourist考场上都没敲出来呢。。。

#include<cstdio>
#include<cstring>
#define N 100005
#define A 10
#define cmin(u,v) (u)>(v)?(u)=(v):0
using namespace std;
namespace runzhe2000
{
    const int INF = 1<<29;
    int ft[N], gt[N], f[N][10], g[N][10], timer = 1, c[10], sta[N], top;
    char s[N]; bool insta[N];
    struct node
    {
        int l, r, v;
    }no[N];
    void del(int x)
    {
        no[no[x].l].r = no[x].r;
        no[no[x].r].l = no[x].l;
    }
    void main()
    {
        int n, q; scanf("%d%d%s",&n,&q,s+1);
        for(int i = 1, v; i <= n; i++)
        {
            if(s[i] >= '0' && s[i] <= '9') v = s[i] - '0'; 
            else v = s[i] == '<' ? 11 : 10;
            no[i] = (node){i-1, i+1, v};
        }
        memset(ft,63,sizeof(ft));
        memset(gt,63,sizeof(gt));
        ++timer; ++n; 
        no[n] = (node){n-1,n+1,10};
        for(int l = 1; l <= n; l++) if(ft[l] > INF)
        {
            for(int cur = l, next, pos = 10; l <= cur && cur <= n; cur = next, ++timer)
            {
                if(timer < ft[cur])
                {
                    ft[cur] = timer;
                    for(int i = 0; i < A; i++)
                        f[cur][i] = c[i];
                }

                if(no[cur].v < 10)
                {
                    c[no[cur].v--]++;
                    if(no[cur].v < 0) del(cur);
                }
                else
                {
                    pos = no[cur].v; 
                }
                next = pos==11?no[cur].l:no[cur].r;
                if(no[cur].v > 9 && no[next].v > 9) del(cur);

                if(!insta[cur])sta[++top] = cur, insta[cur] = 1;

                if(pos == 11)
                {
                    for(; no[cur].l < sta[top] && sta[top] <= cur && top; --top)
                    {
                        gt[sta[top]] = ++timer;
                        for(int i = 0; i < A; i++)
                            g[sta[top]][i] = c[i];
                    }
                }
            }
        }
        for(; q--; )
        {
            int l, r; scanf("%d%d",&l,&r);
            if(ft[r+1] < gt[l]) 
            {
                for(int i = 0; i < 10; i++)
                    printf("%d%c",f[r+1][i] - f[l][i],i==9?'\n':' ');
            }
            else 
            {
                for(int i = 0; i < 10; i++)
                    printf("%d%c",g[l][i] - f[l][i],i==9?'\n':' ');
            }
        }
    }
}
int main()
{
    runzhe2000::main();
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值