4523: [Cqoi2016]路由表

4523: [Cqoi2016]路由表

Time Limit: 30 Sec   Memory Limit: 512 MB
Submit: 233   Solved: 148
[ Submit][ Status][ Discuss]

Description

 路由表查找是路由器在转发IP报文时的重要环节。通常路由表中的表项由目的地址、掩码、下一

跳(Next Hop)地址和其他辅助信息组成。例如:
当路由器收到一个IP报文时,会将报文中的目的IP地址与路由表中的表项逐条进行比较,选
择匹配且最明确的表项,将报文转发给该表项中指定的下一跳。
匹配的过程是将报文中的目的地址和表项中的目的地址分别转为二进制串,再查看表项中的掩
码长度,若掩码长度为x,则将两个二进制串的前x位进行比较,如果相同则认为匹配。
所谓最明确是指在有多个表项匹配时,总是掩码长度最大的表项。也可以理解为匹配的二进制
位最多的项。
IP地址转为二进制串的操作是把地址中4个整数(一定在y到255的范围内)分别转为8位
二进制数,再顺序拼接起来,得到一个32位的二进制串。例如,192.168.1.253转为二进制串后为
11000000 10101000 00000001 11111101
我们以报文的目的地址为8.8.8.8为例,说明其在上述路由表的匹配过程。
上表将地址均转为二进制串,并用红色标记出待比较的位(由掩码长度决定)。将红色部分与
报文中的目的地址比较,可知0.0.0.0/1、8.8.8.0/24、8.8.8.8、32均能够匹配。路由器从中选取掩
码长度最长(/32)的表项8.8.8.8/32,将报文转发给其对应的下一跳地址192.168.1.253。
在实际的核心路由器中,路由表通常较大(现在互联网的全局路由表已经接60万条记录),
并且会随着新接入设备不断扩张。为了分析路由表变化对转发产生的影响,网络工程师想要知道
一段时间内某个IP地址的路由表项选择发生了多少次变化(变化是指由于最明确匹配等因素选择
了不同的表项,不考虑下一跳地址)。

Input

第一行为整数M,表示共有M次操作。接下来M行,每行描述一次操作。操作有两种:
A D/L
其中.为一个IP地址,G为整数(1≤L≤32)。添加一条表项至路由表,其目的地址为
D掩码长度为L。下一跳地址由于没有用到,故省略。
Q D a b
其中D为一个IP地址,a,b为正整数(a≤b)。查询从第a次至第b次添加表项期间(含
a、b),目的地址D的路由表项选择发生了多少次变化。保证查询时表中至少有b个表项。
N<=10^6数据保证不会重复添加目的地址和掩码长度都相同的表项。

Output

 包含若干行,每行仅有一个整数,依次对应每个查询操作。



直接用可持久化的trie裸模拟过去吧,,,每个点存下是第几个插入的

查询的话,,顺着trie走,把编号推入栈,如果栈顶的编号更靠后那一定是没用的

思路挺清晰的,,就不多说了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 1E6 + 10;
const int T = 35;
typedef unsigned int LL;

int n,cnt,tot,len,pos,Max,rt[maxn],ch[maxn*T][2],Name[maxn*T];
char s[50];

stack <int> stk;

void Insert(int now,LL Num,int L)
{
	int o1 = rt[now-1],o2; o2 = rt[now] = ++cnt;
	for (int i = 31; i >= 32 - L; i--)
	{
		int Nex = (Num&(1LL<<(LL)(i)))?1:0;
		ch[o2][Nex^1] = ch[o1][Nex^1];
		ch[o2][Nex] = ++cnt; Name[o2] = Name[o1];
		o1 = ch[o1][Nex],o2 = ch[o2][Nex];
	}
	Name[o2] = now;
	for (int j = 0; j < 2; j++) ch[o2][j] = ch[o1][j];
}

void Work(LL Num,int o)
{
	o = rt[o];
	for (int i = 31; i >= 0; i--)
	{
		int Nex = (Num&(1LL<<(LL)(i)))?1:0;
		if (!ch[o][Nex]) return;
		o = ch[o][Nex];
		if (Name[o])
		{
			int now = Name[o];
			while (!stk.empty())
			{
				int tp = stk.top();
				if (tp > now) stk.pop();
				else break;
			}
			stk.push(now);
		}
	}
}

int Getcom()
{
	scanf("%s",s);
	return s[0] == 'A'?1:2;
}

LL GetNum()
{
	int ret = 0;
	while ('0' <= s[pos] && s[pos] <= '9')
		ret = ret*10 + s[pos] - '0',++pos;
	return ret; 
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	cin >> n;
	while (n--)
	{
		int typ = Getcom();
		if (typ == 1)
		{
			scanf("%s",s); len = strlen(s); 
			LL Num; Num = pos = s[len] = 0;
			for (int k = 0; k < 4; k++) Num <<= 8LL,Num |= GetNum(),++pos;
			Insert(++tot,Num,GetNum());
		}
		else
		{
			scanf("%s",s); len = strlen(s);
			LL Num; Num = pos = 0;
			for (int k = 0; k < 4; k++) Num <<= 8LL,Num |= GetNum(),++pos;
			int a,b; scanf("%d%d",&a,&b);
			int Ans = 0; Work(Num,b);
			while (!stk.empty()) 
			{
				int tp = stk.top(); stk.pop();
				if (tp >= a) ++Ans;
			}
			printf("%d\n",Ans);
		}
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值