Codeforces Round #457(Div.2)Problem D Jamie and To-do List(可持久化Trie树)

D. Jamie and To-do List time limit per test 2 seconds memory limit per test 512 megabytes input standard input output standard output

Why I have to finish so many assignments???

Jamie is getting very busy with his school life. He starts to forget the assignments that he has to do. He decided to write the things down on a to-do list. He assigns a value priority for each of his assignment (lower value means more important) so he can decide which he needs to spend more time on.

After a few days, Jamie finds out the list is too large that he can’t even manage the list by himself! As you are a good friend of Jamie, help him write a program to support the following operations on the to-do list:

  • set ai xi — Add assignment ai to the to-do list if it is not present, and set its priority to xi. If assignment ai is already in the to-do list, its priority is changed to xi.
  • remove ai — Remove assignment ai from the to-do list if it is present in it.
  • query ai — Output the number of assignments that are more important (have a smaller priority value) than assignment ai, so Jamie can decide a better schedule. Output  - 1 if ai is not in the to-do list.
  • undo di — Undo all changes that have been made in the previous di days (not including the day of this operation)

At day 0, the to-do list is empty. In each of the following q days, Jamie will do exactly one out of the four operations. If the operation is a query, you should output the result of the query before proceeding to the next day, or poor Jamie cannot make appropriate decisions.

Input

The first line consists of a single integer q (1 ≤ q ≤ 105) — the number of operations.

The following q lines consists of the description of the operations. The i-th line consists of the operation that Jamie has done in the i-th day. The query has the following format:

The first word in the line indicates the type of operation. It must be one of the following four: setremovequeryundo.

  • If it is a set operation, a string ai and an integer xi follows (1 ≤ xi ≤ 109)ai is the assignment that need to be set to priority xi.
  • If it is a remove operation, a string ai follows. ai is the assignment that need to be removed.
  • If it is a query operation, a string ai follows. ai is the assignment that needs to be queried.
  • If it is a undo operation, an integer di follows (0 ≤ di < i)di is the number of days that changes needed to be undone.

All assignment names ai only consists of lowercase English letters and have a length 1 ≤ |ai| ≤ 15.

It is guaranteed that the last operation is a query operation.

Output

For each query operation, output a single integer — the number of assignments that have a priority lower than assignment ai, or  - 1 if aiis not in the to-do list.

Interaction

If the operation is a query, you should output the result of the query and flush the output stream before proceeding to the next operation. Otherwise, you may get the verdict Idleness Limit Exceed.

For flushing the output stream, please refer to the documentation of your chosen programming language. The flush functions of some common programming languages are listed below:

  • C: fflush(stdout);
  • C++: cout « flush;
  • Java: System.out.flush();

Examples input

8
set chemlabreport 1
set physicsexercise 2
set chinesemockexam 3
query physicsexercise
query chinesemockexam
remove physicsexercise
query physicsexercise
query chinesemockexam

output

1
2
-1
1

input

8
set physicsexercise 2
set chinesemockexam 3
set physicsexercise 1
query physicsexercise
query chinesemockexam
undo 4
query physicsexercise
query chinesemockexam

output

0
1
0
-1

input

5
query economicsessay
remove economicsessay
query economicsessay
undo 2
query economicsessay

output

-1
-1
-1

input

5
set economicsessay 1
remove economicsessay
undo 1
undo 1
query economicsessay

output

-1

【思路】

题目要求实现对字符串和优先级的匹配、解除匹配、对比某优先级更高的字符串计数、撤销前面若干步这四种操作。

第一时间往平衡树方面想,但是并沒法做undo这个操作,做过这道题的同学告诉我:你想多了,这是两棵可持久化的Trie树。

下面是思考的总结:对于所有操作,必定需要记录下操作的结果,才能实现undo,而且满足这个条件时不应过分使用空间。我们可以用一个root指针数组保存每一步操作后的Trie树状态,如果undo x则意味着退到x个状态之前的那个状态,那么指针指向前面的root;如果是执行修改操作,则复制一下上一个状态再修改;如果是执行询问操作,则指针指向上一个root。Trie树在这里的作用,无非就是对应上字符串和优先级,对比某优先级大(也就是数值小)的字符串统计,时间复杂度为O(logN)级别。


【代码】

//************************************************************************
// File Name: main.cpp
// Author: Shili_Xu
// E-Mail: shili_xu@qq.com 
// Created Time: 2018年02月02日 星期五 16时28分44秒
//************************************************************************

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAXN = 1e5 + 5;

struct string_trie {
	int data;
	string_trie *child[26];

	string_trie()
	{
		data = 0; 
		for (int i = 0; i < 26; i++) child[i] = NULL;
	}

	string_trie(string_trie *another)
	{
		data = another->data;
		for (int i = 0; i < 26; i++) child[i] = another->child[i];
	}
};

struct binary_trie {
	int data;
	binary_trie *child[2];

	binary_trie()
	{
		data = 0;
		child[0] = child[1] = NULL;
	}

	binary_trie(binary_trie *another)
	{
		data = another->data;
		child[0] = another->child[0];
		child[1] = another->child[1];
	}
};

string_trie *string_root[MAXN];
binary_trie *binary_root[MAXN];
int q;

void insert_string(int i, char *s, int x)
{
	string_trie *p = string_root[i];
	while (*s != '\0') {
		int id = *s - 'a';
		if (p->child[id])
			p->child[id] = new string_trie(p->child[id]);
		else
			p->child[id] = new string_trie();
		p = p->child[id];
		s++;
		if (*s == '\0') p->data = x;
	}
}

void insert_num(int i, int num, int x)
{
	binary_trie *p = binary_root[i];
	for (int j = 30; j >= 0; j--) {
		int id = (num >> j) & 1;
		if (p->child[id])
			p->child[id] = new binary_trie(p->child[id]);
		else
			p->child[id] = new binary_trie();
		p = p->child[id];
		p->data += x;
	}
}

int search(int i, char *s)
{
	string_trie *p = string_root[i];
	while (*s != '\0') {
		int id = *s - 'a';
		if (!p->child[id]) return 0;
		p = p->child[id];
		s++;
	}
	return p->data;
}

int query(int i, int num)
{
	binary_trie *p = binary_root[i];
	int ans = 0;
	for (int j = 30; j >= 0; j--) {
		int id = (num >> j) & 1;
		if (id && p->child[0]) ans += p->child[0]->data;
		p = p->child[id];
	}
	return ans;
}

int main()
{
	string_root[0] = new string_trie();
	binary_root[0] = new binary_trie();
	scanf("%d", &q);
	for (int i = 1; i <= q; i++) {
		char mes[6], str[16];
		int x;
		scanf("%s", mes);
		if (mes[0] == 's') {
			scanf("%s %d", str, &x);
			string_root[i] = new string_trie(string_root[i - 1]);
			binary_root[i] = new binary_trie(binary_root[i - 1]);
			int old = search(i, str);
			if (old != x) {
				insert_string(i, str, x);
				if (old) insert_num(i, old, -1);
				insert_num(i, x, 1);
			}
		}
		if (mes[0] == 'r') {
			scanf("%s", str);
			string_root[i] = new string_trie(string_root[i - 1]);
			binary_root[i] = new binary_trie(binary_root[i - 1]);
			int old = search(i, str);
			if (old) {
				insert_string(i, str, 0);
				insert_num(i, old, -1);
			}
		}
		if (mes[0] == 'q') {
			scanf("%s", str);
			string_root[i] = string_root[i - 1];
			binary_root[i] = binary_root[i - 1];
			int old = search(i, str);
			if (old)
				printf("%d\n", query(i, old));
			else
				printf("-1\n");
		}
		if (mes[0] == 'u') {
			scanf("%d", &x);
			string_root[i] = string_root[i - x - 1];
			binary_root[i] = binary_root[i - x - 1];
		}
		fflush(stdout);
	}
	return 0;
}
    原文作者:Trie树
    原文地址: https://blog.csdn.net/Shili_Xu/article/details/79283098
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞