这个提答还算小清新,不用跑很久,主要是观察数据性质。

Description

小 Q 最近发现了一款新游戏,游戏的目标是从一个新手修炼成为武功高强的大侠。

对着这个游戏,小 Q 玩了很多次仍然玩不出他想要的结局,于是他费尽千辛万苦找到了游戏的剧本。令人惊讶的是,游戏的剧本并不像我们平时见到的剧本,反而很像代码。这个剧本是这样描述的:

量:有 $2$ 种量,常数和变量。

常数:一个整数。

变量:初始值为 $0$ 的可变整数,不同变量用不同正整数区分。

事件:整个剧本由若干个事件构成。所有的事件按照给定的顺序从 $1$ 开始依次编号。事件共有 $3$ 种:普通事件、选择跳转和条件跳转。

执行位置:一个整数,表示接下来将会执行的事件编号,如果不存在这个编号的事件则停止,即游戏到了一个结局。最初的时候执行位置为 $1$。

普通事件:一个变量增加或减少一个量的值。之后执行位置增加 $1$。

选择跳转:两个整数。执行到这里时玩家需要在这两个整数中选择一个,之后执行位置将被修改为这个整数。

条件跳转:两个量和两个整数。执行到这里时,若第一个量小于第二个量,则执行位置将被修改为第一个整数,否则将被修改为第二个整数。

小 Q 认为,整个游戏是希望一个叫做“成就值”的变量(编号为 $1$)最大。

现在给出 $10$ 个剧本,需要求出每个剧本获得最大成就值的每一步选择。

Solution

这个题乍看上去除了 $2^x$ 的搜索没啥办法能做,去看看数据,选择跳转的次数是非常多的。

不知道模拟退火效果怎么样,我估计可以得到一定分数,但无法 AC。

就看看数据吧。

前两个点可以直接搜索过去。

第三个点挺整齐的,发现每 $170$ 行之间是独立的,在进入下一个之前会把所有变量的绝对值加到变量 $1$ 上并清空所有变量。

那么我们对每个块内搜索就好了,最后答案就是每个块内的最大值加起来。

第三个点的代码:

namespace solve
{
    const int maxn = 40000;

    int var[maxn], n, m;

    struct number
    {
        char type;
        int x;
        inline int val() { return type == 'c' ? x : var[x]; }
        inline void in() { type = readchar(), x = read(); }
    };

    struct opt
    {
        char type;
        int s1, s2;
        int mul;
        number a, b;
        inline void in(int id)
        {
            type = readchar();
            if (type == 's')
                s1 = read(), s2 = read();
            else if (type == 'i')
                a.in(), b.in(), s1 = read(), s2 = read();
            else
            {
                s1 = id + 1;
                a.type = 'v', a.x = read(), mul = readchar() == '+' ? 1 : -1;
                b.in();
            }
        }
    } a[maxn];

    int choice[maxn];
    int ans_choice[maxn], ans, tot;

    void dfs(int now, int dep, int i)
    {
        if (now > (i + 1) * 170)
        {
            if (var[1] > ans)
            {
                ans = var[1];
                for (int i = 1; i < dep; i++)
                    ans_choice[i] = choice[i];
                tot = dep - 1;
            }
            return;
        }
        if (a[now].type == 's')
        {
            choice[dep] = 1, dfs(a[now].s1, dep + 1, i);
            choice[dep] = 2, dfs(a[now].s2, dep + 1, i);
        }
        else if (a[now].type == 'i')
        {
            if (a[now].a.val() < a[now].b.val())
                dfs(a[now].s1, dep, i);
            else
                dfs(a[now].s2, dep, i);
        }
        else
        {
            int tmp = a[now].mul * a[now].b.val();
            var[a[now].a.x] += tmp;
            dfs(now + 1, dep, i);
            var[a[now].a.x] -= tmp;
        }
    }

    void main()
    {
        n = read(), m = read();
        for (int i = 1; i <= n; i++)
            a[i].in(i);

        int all = 0;
        for (int i = 0; i < 200; i++)
        {
            dfs(i * 170 + 1, 1, i);
            for (int i = 1; i <= tot; i++)
                cout << ans_choice[i] << endl;
            all += ans;
            memset(var, 0, sizeof(var)), ans = 0;
        }
        cerr << all << endl;
    }
}

第 456 个点可以发现都是很多下面这种形式组合起来:


i v 2 c 10 3 4
i c 0 c 1 8 0
s 5 8
v 2 - c 10
v 1 + c 22750
i c 0 c 1 8 0

意思就是,变量 2 有个初值,然后如果变量 2 的值大于 $x$,可以让变量 2 的值减去 $x$,同时变量 1 的值加上一个数,也可以不选。

发现每次跳的地方都比这个地方大,也就是说这是个 DAG,我们倒着跑一个背包即可。

第 78910 个点发现也是由 456 点那样的大块组成的,然后大块内部是类似第三个点的结构,那就大块内搜索,然后背包就好了。

注意不能用行数分块,数据好像并不是完全均匀的,以及价值之类的要算清楚。

直接给出 7 8 9 10 的代码:


namespace solve
{
    const int maxn = 40000;

    int var[maxn], n, m;

    struct number
    {
        char type;
        int x;
        inline int val() { return type == 'c' ? x : var[x]; }
        inline void in() { type = readchar(), x = read(); }
    };

    struct opt
    {
        char type;
        int s1, s2;
        int mul;
        number a, b;
        inline void in(int id)
        {
            type = readchar();
            if (type == 's')
                s1 = read(), s2 = read();
            else if (type == 'i')
                a.in(), b.in(), s1 = read(), s2 = read();
            else
            {
                s1 = id + 1;
                a.type = 'v', a.x = read(), mul = readchar() == '+' ? 1 : -1;
                b.in();
            }
        }
    } a[maxn];

    int id[maxn], cnt;
    int val[maxn], w[maxn], nxt[maxn];
    int is[3000][10010];
    int f[3000][10010];

    vector<int> all[maxn];

    namespace dfs
    {
        int ans, ans_choice[maxn], tot;
        int choice[maxn];

        void dfs(int now, int dep, int ed)
        {
            if (now > ed) 
            {
                if (var[1] > ans)
                {
                    ans = var[1];
                    for (int i = 1; i < dep; i++)
                        ans_choice[i] = choice[i];
                    tot = dep - 1;
                }
                return;
            }
            if (a[now].type == 's')
            {
                choice[dep] = 1, dfs(a[now].s1, dep + 1, ed);
                choice[dep] = 2, dfs(a[now].s2, dep + 1, ed);
            }
            else if (a[now].type == 'i')
            {
                if (a[now].a.val() < a[now].b.val())
                    dfs(a[now].s1, dep, ed);
                else
                    dfs(a[now].s2, dep, ed);
            }
            else
            {
                int tmp = a[now].mul * a[now].b.val();
                var[a[now].a.x] += tmp;
                dfs(now + 1, dep, ed);
                var[a[now].a.x] -= tmp;
            }
        }
        void work(int bg, int ed, int i)
        {
            memset(var, 0, sizeof(var)), ans = 0;
            memset(ans_choice, 0, sizeof(ans_choice)), tot = 0;
            memset(choice, 0, sizeof(choice));
            dfs(bg, 1, ed);
            // cerr << bg << " " << ed << " " << i << " " << ans << endl;
            for (int j = 1; j <= tot; j++)
                all[i].push_back(ans_choice[j]);
        }
    }

    vector<int> choice;

    void choose(int id)
    {
        for (int p : all[id])
            choice.push_back(p);
    }

    void back(int i, int j)
    {
        // cerr << i << " " << j << endl;
        if (i > cnt)
            return;
        if (is[i][j])
        {
            choice.push_back(1), choose(i + 1);
            back(i + 1, j - w[i + 1]);
        }
        else
        {
            if (j >= w[i + 1])
                choice.push_back(2);
            back(nxt[i], j);
        }
    }

    int isbg[maxn];

    void main()
    {
        n = read(), m = read();
        for (int i = 1; i <= n; i++)
            a[i].in(i);
        int W = a[1].b.val();
        for (int i = 1; i <= n - 2; i++)
        {
            if (a[i].type == 's' && a[i + 1].type == 'v' && a[i + 2].type == 's')
            {
                isbg[i] = 1;
                id[i] = ++cnt;
            }
        }
        id[n + 1] = cnt + 1;
        w[cnt + 1] = 1e9;
        cerr << cnt << endl;
        for (int i = n; i >= 1; i--)
        {
            if (!id[i])
                id[i] = id[i + 1];
        }
        for (int i = 1; i <= n; i++)
        {
            if (isbg[i])
            {
                dfs::work(i + 2, a[i].s2 - 2, id[i] + 1);
                val[id[i] + 1] = dfs::ans;
                w[id[i] + 1] = a[i - 2].b.val();
                nxt[id[i]] = id[a[i].s2];
            }
        }
        for (int i = 1; i <= cnt; i++)
            if (!nxt[i])
                nxt[i] = cnt + 1;
        // for (int i = 1; i <= cnt; i++)
            // cerr << nxt[i] << endl;
        for (int i = cnt; i >= 1; i--)
        {
            memcpy(f[i], f[nxt[i]], sizeof(f[i]));
            for (int j = W; j >= w[i + 1]; j--)
            {
                if (f[i + 1][j - w[i + 1]] + val[i + 1] > f[i][j])
                {
                    f[i][j] = f[i + 1][j - w[i + 1]] + val[i + 1];
                    is[i][j] = 1;
                }
            }
        }
        // for (int i = 1; i <= cnt + 1; i++)
            // cerr << w[i] << " " << val[i] << endl;
        int j = max_element(f[1], f[1] + W + 1) - f[1], res = f[1][j];
        cerr << j << " " << res << endl;
        back(1, j);
        for (int p : choice)
            cout << p << endl;
    }
}