JZOJ 5753 完全二叉树

传送门
题面

思路

首先,前 30 分就是一个暴力 Trie 树。但是这题应该是个 fake 题,因为在没有进行操作 1 时答案居然为 1 而不是 0 !!!果然这道题的意思就是一个 Trie 树吗???

发现,对于中间的 Special Instance,不会发生取模操作,因此答案就是每次进位后停下来的位置的和,特别地,第一次操作的答案为 n + 1 。按理说这么做有 70 分,但是我却得到了 90 分。唉,我太多弱了,要不是这多出来的 20 分,我连 200 都上不到,这可是要垫底了。唉,我太弱啦!

参考代码(70 分)
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef LL INT_PUT;
INT_PUT readIn()
{
    INT_PUT a = 0; bool positive = true;
    char ch = getchar();
    while (!(ch == '-' || std::isdigit(ch))) ch = getchar();
    if (ch == '-') { positive = false; ch = getchar(); }
    while (std::isdigit(ch)) { a = a * 10 - (ch - '0'); ch = getchar(); }
    return positive ? -a : a;
}
void printOut(INT_PUT x)
{
    char buffer[20]; int length = 0;
    if (x < 0) putchar('-'); else x = -x;
    do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
    do putchar(buffer[--length]); while (length);
    putchar('\n');
}

const int maxn = int(1e5) + 5;
int n, q;

#define RunInstance(x) delete new x
struct brute
{
    static const int maxN = 1000;
    int num[maxN];
    struct Trie
    {
        int size;
        int ch[maxN * maxN][2];
        Trie() : size(1), ch() {}
        void insert(int* num, int digit)
        {
            int cnt = 0;
            while (digit--)
            {
                if (!ch[cnt][num[digit]])
                {
                    ch[cnt][num[digit]] = size++;
                }
                cnt = ch[cnt][num[digit]];
            }
        }
    } trie;

    brute() : num()
    {
        while (q--)
        {
            int type = readIn();
            if (type == 1)
            {
                int c = readIn();
                num[c]++;
                while (num[c] > 1 && c < n)
                {
                    num[c + 1]++;
                    num[c] = 0;
                    c++;
                }
                trie.insert(num, n);
            }
            else if (type == 2)
            {
                printOut(trie.size);
            }
        }
    }
};
struct cheat
{
    struct Query
    {
        int type;
        int c;
        void read()
        {
            type = readIn();
            if (type == 1) c = readIn();
        }
    } querys[maxn];
    int num[maxn];

    cheat() : num()
    {
        for (int i = 1; i <= q; i++)
            querys[i].read();
        LL ans = 1;
        for (int i = 1; i <= q; i++)
        {
            const Query& Q = querys[i];
            if (Q.type == 1)
            {
                int c = Q.c;
                num[c]++;
                while (num[c] > 1 && c < n)
                {
                    num[c + 1]++;
                    num[c] = 0;
                    c++;
                }

                if (ans == 1)
                    ans = n + 1;
                else
                    ans += c + 1;
            }
            else
            {
                printOut(ans);
            }
        }
    }
};

void run()
{
    n = readIn();
    q = readIn();
    if (n <= 500)
        RunInstance(brute);
    else
        RunInstance(cheat);
}

int main()
{
#ifndef LOCAL
    freopen("tree.in", "r", stdin);
    freopen("tree.out", "w", stdout);
#endif
    run();
    return 0;
}
正解

回忆 Special Instance 中我们是怎么求答案的:我们实际上是求新串与上一个串的 LCP,最终的答案就是 n 减去 LCP 的长度。但为什么我没有有所突破呢?因为这个操作的实质是:求新串与之前所有串的 LCP 的最大值。当然,这个东西说出来就显而易见了,但是考试时却并没有转换过来。

反过来想,为什么考试时我没有想到呢?一是因为我太弱了,二是因为之前所有串的 LCP 的最大值都是上一个数贡献的。这说明了什么呢?说明最大 LCP 长度一定是差与它最小的两个数(一正一负)贡献的

那么我们只需要每次找到差与它最小的两个数。现在我们可以在 O ( log n ) 的时间复杂度内比较两个数的大小(用 Hash),所以理论上我们可以在 O ( n log 2 n ) 的时间复杂度内解决这个问题(使用可持久化数组,维护 Hash 值)。

参考代码
// 我太弱了,还没有写。

猜你喜欢

转载自blog.csdn.net/lycheng1215/article/details/80514004