传送门
题面
思路
首先,前 分就是一个暴力 Trie 树。但是这题应该是个 fake 题,因为在没有进行操作 时答案居然为 而不是 !!!果然这道题的意思就是一个 Trie 树吗???
发现,对于中间的 Special Instance,不会发生取模操作,因此答案就是每次进位后停下来的位置的和,特别地,第一次操作的答案为 。按理说这么做有 分,但是我却得到了 分。唉,我太多弱了,要不是这多出来的 分,我连 都上不到,这可是要垫底了。唉,我太弱啦!
参考代码(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,最终的答案就是 减去 LCP 的长度。但为什么我没有有所突破呢?因为这个操作的实质是:求新串与之前所有串的 LCP 的最大值。当然,这个东西说出来就显而易见了,但是考试时却并没有转换过来。
反过来想,为什么考试时我没有想到呢?一是因为我太弱了,二是因为之前所有串的 LCP 的最大值都是上一个数贡献的。这说明了什么呢?说明最大 LCP 长度一定是差与它最小的两个数(一正一负)贡献的。
那么我们只需要每次找到差与它最小的两个数。现在我们可以在 的时间复杂度内比较两个数的大小(用 Hash),所以理论上我们可以在 的时间复杂度内解决这个问题(使用可持久化数组,维护 Hash 值)。
参考代码
// 我太弱了,还没有写。