目录
显示
题目链接
https://www.luogu.com.cn/problem/P5768
题解
本题容易看出是一个在 Trie 树上的匹配问题。
对于每个添加操作,我们只需按题意将指定 ip 的前 \(x\) 位插入即可(\(x\) 位后面的内容显然没有意义),并在结束位置打一个标记 \(ed\),表示这是第 \(ed\) 条表项。
对于查询操作,题目最后的提示告诉我们可以差分。
也就是说一个查询 \([l,r]\) 可以变成两个查询 \([1,r]\) 和 \([1,l−1]\),最后把结果相减即可。
我们现在考虑如何解决一个 \([1,x]\) 类型的查询。
我们还是在 Trie 树上走,并用一个单调栈维护我们遇到的标记。每遇到一个有效标记(在查询范围内),就先将栈内时间比它靠后的标记弹出(这些标记都是无用的),再将该标记插入栈中。
最后结果就是栈内的元素数目。
#include <cstdio>
#include <stack>
using namespace std;
typedef unsigned ui;
struct trie
{
struct node
{
int son[2],ed;
}p[10000005];
int tot=1,id=0;
void insert(ui ip,int len)
{
int u=1;
for(int i=31;32-i<=len;i--)
{
int v=(ip>>i)&1;
if(p[u].son[v])u=p[u].son[v];
else
{
p[u].son[v]=++tot;
u=tot;
}
}
p[u].ed=++id;
}
int query(ui ip,int t)
{
stack<int> s;
int u=1,pos=31;
while(1)
{
if(p[u].ed&&p[u].ed<=t)
{
int ed=p[u].ed;
while(!s.empty()&&ed<s.top())
s.pop();
s.push(ed);
}
int v=(ip>>pos)&1;
if(!p[u].son[v])break;
else u=p[u].son[v];
pos--;
}
return s.size();
}
}tr;
char op[5];
ui getip()
{
ui ip=0;
int a,b,c,d;
scanf("%d.%d.%d.%d/",&a,&b,&c,&d);
ip=(ip<<8)+a,ip=(ip<<8)+b,ip=(ip<<8)+c,ip=(ip<<8)+d;
return ip;
}
int main()
{
int m;
scanf("%d",&m);
while(m--)
{
scanf("%s",op);
if(op[0]=='A')
{
ui ip=getip();
int len;
scanf("%d",&len);
tr.insert(ip,len);
}
else
{
ui ip=getip();
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",tr.query(ip,b)-tr.query(ip,a-1));
}
}
return 0;
}