[LOJ2046]路由表

题目

传送门 to LOJ

题意概要
你在聆听戴夫的教诲。戴夫的一句话可以抽象为一个二进制串,因为可以听出来的词太少。两句话的“匹配度”是它们的最长公共前缀。

对于戴夫的一句话,你会在记忆中搜寻他已说过的一句,满足二者的“匹配度”最高。将这个他已经说过的话称为“引言”。现在每秒钟可能发生两个事件中的一个:

  1. 戴夫说了一句话,你把它给记住了。
  2. 戴夫说了一句话 S S S 。你感觉似乎之前听过。你想知道,从第 a a a 秒开始,一直到第 b b b 秒,随着你记住的话不断变多, S S S 的“引言”改变了多少次?但是你不会记住这个事件中的话,因为你忙着回忆了。

一共有 M M M 秒,把每个事件二的答案都告诉戴夫吧!虽然他听不懂。

数据范围与提示
M ≤ 1 0 6 M\le 10^6 M106

思路

高级 の の 暴力

先不管题目到底在问什么,只考虑怎样求 当前某个地址的匹配(并且可以快速加入)。

发现就是 0 - 1    t r i e 0\text{-}1\;\tt{trie} 0-1trie 树板题。暴力加入。 O ( M 2 ) \mathcal O(M^2) O(M2) 。肯定过不了。

单调栈优化

慢就慢在查询上。

由于没有删地址的操作,某个地址的匹配一定是越来越优,所以这些匹配一定是在 t r i e \tt{trie} trie 树的某条链上。

每次用一个单调栈,维护时间的单调性,最后查栈里有几个 t ∈ [ a , b ] t\in [a,b] t[a,b]

单次操作 O ( 32 ) \mathcal O(32) O(32) ,战斗结束。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
inline int readint(){
	int a = 0, f = 1; char c = getchar();
	for(; c<'0' or c>'9'; c=getchar())
		if(c == '-') f = -1;
	for(; '0'<=c and c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

class Trie{
	struct Node{
		Node* son[2];
		int data;
		Node(){
			son[0] = son[1] = NULL;
			data = 0;
		}
	};
	Node* root;
public:
	Trie(){ root = new Node(); }
	void Add(unsigned x,int len,int id){
		Node* o = root;
		for(int i=31; i>31-len; --i){
			int d = x>>i&1;
			if(o->son[d] == NULL)
				o->son[d] = new Node();
			o = o->son[d];
		}
		o->data = id;
	}
	vector<int> Query(unsigned x){
		vector<int> v; v.clear();
		Node* o = root;
		for(int i=31; ~i; --i){
			int d = x>>i&1;
			if(o->son[d] == NULL)
				break;
			o = o->son[d];
			if(o->data){
				while(not v.empty() and o->data < v.back())
					v.pop_back();
				v.push_back(o->data);
			}
		}
		return v;
	} 
}xez;

int main(){
	int T = readint();
	for(int i=1,zxy=1,ppl; i<=T; ++i){
		char cmd = getchar();
		ppl = 0;
		for(int j=0; j<4; ++j)
			ppl = (ppl<<8|readint());
		if(cmd == 'A'){
			int len = readint();
			xez.Add(ppl,len,zxy ++);
		}
		if(cmd == 'Q'){
			vector<int> &&v = xez.Query(ppl);
			int cnt = 0, a = readint(), b = readint();
			for(; not v.empty(); v.pop_back())
				if(a <= v.back() and v.back() <= b)
					++ cnt;
			printf("%d\n",cnt);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值