[BJOI2016] IP地址 题解
前言
来个不一样的做法,用到了 Trie 树和主席树,并且是可爱的在线算法。
题目链接:洛谷。
题目分析
对于一个查询 \(\texttt{ip}\),只考虑所有前缀字符串规则。以时间建里横轴,匹配长度为纵轴,建出坐标系。易知坐标系中有 \(\Theta(n)\) 条线段。对于某一时刻 \(t\),根据题意,生效的匹配就是越过 \(t\) 的纵坐标最大的那条线段对应的前缀。用样例画张图吧。

红色的线代表每一个时刻生效的匹配,绿色框内就是发生变化的时刻,分别是第 \(3\) 次操作后,第 \(4\) 次操作后,第 \(5\) 次操作后。
考虑使用 01-Trie 解决前缀。从根节点往下一直到匹配不上,每次将这个前缀出现的所有时段赋值为这个前缀的长度,就得到了红色的折线。最后查询 \([l, r]\) 内红色的线的纵坐标变化了多少次。
这个非常线段树啊,区间覆盖,合并信息也很容易,或者用珂朵莉树也可以。但是,直接这样做时间上是错误的,为什么?因为枚举这个前缀出现的所有时段是 \(\Theta(n)\) 的,可以轻松卡到 \(\Theta(nq \log n)\)。但是,原数据太水了,可以见这个帖子。
那么,做法呼之欲出了,预处理的时候把线段树可持久化就行了。但是要注意内存回收,注意结构体内存对齐,或者使用 #pragma pack(1),空间有些紧的。
这个算法时间复杂度是 \(\Theta((n + q)(w + \log n))\),空间复杂度是 \(\Theta(n (w + \log n))\)。
代码
略去了快读。
// #pragma GCC optimize(3)
// #pragma GCC optimize("Ofast", "inline", "-ffast-math")
// #pragma GCC target("avx", "sse2", "sse3", "sse4", "mmx")
#include <iostream>
#include <cstdio>
#define debug(a) cerr << "Line: " << __LINE__ << " " << #a << endl
#define print(a) cerr << #a << "=" << (a) << endl
#define file(a) freopen(#a".in", "r", stdin), freopen(#a".out", "w", stdout)
#define main Main(); signed main(){ return ios::sync_with_stdio(0), cin.tie(0), Main(); } signed Main
using namespace std;
#include <vector>
#include <bitset>
int n, q;
struct node{
int son[2];
vector<pair<int, int> > tim;
} tree[100010 * 20];
int tot;
void insert(char str[], int timer){
int now = 0;
for (int i = 0; str[i]; ++i){
int t = str[i] - '0';
if (!tree[now].son[t]) tree[now].son[t] = ++tot;
now = tree[now].son[t];
}
tree[now].tim.push_back({timer, n});
}
void erase(char str[], int timer){
int now = 0;
for (int i = 0; str[i]; ++i){
int t = str[i] - '0';
now = tree[now].son[t];
}
int s = tree[now].tim.back().first;
tree[now].tim.pop_back();
tree[now].tim.push_back({s, timer});
}
struct President_Segment_Tree{
struct node{
int lson, rson;
int val, lval, rval;
};
static node tree[200010 * 80];
static int tot;
static bitset<200010 * 80> tag;
static inline void init(){
tot = 0;
}
static inline int newNode(){
int res = ++tot;
tree[res] = {0, 0, 0, 0, 0};
return res;
}
static inline int copyNode(int idx){
int res = newNode();
return tree[res] = tree[idx], tag[res] = tag[idx], res;
}
static inline void pushup(int idx){
tree[idx].lval = tree[tree[idx].lson].lval;
tree[idx].rval = tree[tree[idx].rson].rval;
tree[idx].val = tree[tree[idx].lson].val + tree[tree[idx].rson].val + (tree[tree[idx].lson].rval != tree[tree[idx].rson].lval);
}
static inline void pushtag(int idx, int v){
tag.set(idx);
tree[idx].lval = tree[idx].rval = v;
tree[idx].val = 0;
}
static inline void pushdown(int idx){
if (!tag[idx]) return;
pushtag(tree[idx].lson = copyNode(tree[idx].lson), tree[idx].lval);
pushtag(tree[idx].rson = copyNode(tree[idx].rson), tree[idx].lval);
tag.reset(idx);
}
int root[100010 * 20];
void build(int &idx, int l, int r){
idx = newNode();
if (l == r) return;
int mid = (l + r) >> 1;
build(tree[idx].lson, l, mid);
build(tree[idx].rson, mid + 1, r);
pushup(idx);
}
void modify(int &idx, int trl, int trr, int l, int r, int val){
if (trl > r || trr < l) return;
idx = copyNode(idx);
if (l <= trl && trr <= r) return pushtag(idx, val);
pushdown(idx);
int mid = (trl + trr) >> 1;
modify(tree[idx].lson, trl, mid, l, r, val);
modify(tree[idx].rson, mid + 1, trr, l, r, val);
pushup(idx);
}
struct Q{ int val, lval, rval; };
Q add(const Q & a, const Q & b){
if (a.val == -1) return b;
if (b.val == -1) return a;
return {
a.val + b.val + (a.rval != b.lval),
a.lval, b.rval
};
}
Q query(int idx, int trl, int trr, int l, int r){
if (trl > r || trr < l) return {-1, 0, 0};
if (l <= trl && trr <= r) return {tree[idx].val, tree[idx].lval, tree[idx].rval};
pushdown(idx);
int mid = (trl + trr) >> 1;
return add(query(tree[idx].lson, trl, mid, l, r), query(tree[idx].rson, mid + 1, trr, l, r));
}
} yzh;
President_Segment_Tree::node President_Segment_Tree::tree[200010 * 80];
int President_Segment_Tree::tot;
bitset<200010 * 80> President_Segment_Tree::tag;
void dfs(int now, int dpt = 0){
for (const auto & [s, e] : tree[now].tim){
yzh.modify(yzh.root[now], 1, n, s, e, dpt);
}
tree[now].tim.clear();
tree[now].tim.shrink_to_fit();
if (tree[now].son[0]){
yzh.root[tree[now].son[0]] = yzh.root[now];
dfs(tree[now].son[0], dpt + 1);
}
if (tree[now].son[1]){
yzh.root[tree[now].son[1]] = yzh.root[now];
dfs(tree[now].son[1], dpt + 1);
}
}
signed main(){
read(n, q);
for (int i = 1; i <= n; ++i){
static char op[5], str[50];
read(op, str);
if (*op == 'A') insert(str, i);
else erase(str, i - 1);
}
dfs(0);
for (int i = 1, l, r; i <= q; ++i){
static char str[50];
read(str, l, r);
int now = 0;
for (int j = 0; str[j]; ++j){
int t = str[j] - '0';
if (!tree[now].son[t]) break;
now = tree[now].son[t];
}
write(yzh.query(yzh.root[now], 1, n, l, r).val, '\n');
}
return 0;
}
[BJOI2016] IP地址 题解的更多相关文章
- BJOI2016 IP地址
题目链接 Description 给定 \(n\) 个 \(01\) 模式串.\(q\) 次询问: 每次询问给定一个 \(01\) 串: 设给这个串匹配的串是在模式串中存在的他的最长前缀 问 \([a ...
- LeetCode:复原IP地址【93】
LeetCode:复原IP地址[93] 题目描述 给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式. 示例: 输入: "25525511135" 输出: [&qu ...
- leetcode-解题记录 1108. IP 地址无效化
题目: 给你一个有效的 IPv4 地址 address,返回这个 IP 地址的无效化版本. 所谓无效化 IP 地址,其实就是用 "[.]" 代替了每个 ".". ...
- CentOS:ECDSA host key "ip地址" for has changed and you have requested strict checking(转)
原文地址:http://blog.csdn.net/ausboyue/article/details/52775281 Linux SSH命令错误:ECDSA host key "ip地址& ...
- 烂泥:VMWare Workation双网卡配置IP地址
本文由ilanniweb提供友情赞助,首发于烂泥行天下 想要获得更多的文章,可以关注我的微信ilanniweb 前几天给一个客户做远程项目实施,客户那边的服务器是Windows OS的,我们这边的业务 ...
- xamarin android,UWP 网络类型和IP地址
App开发经常要判断网络连通情况,并判断网络类型,获取网络IP.xamarin中可以使用Dependencies提供各平台下的方法,现把各平台代码记录如下: using System; using S ...
- C#服务器获取客户端IP地址以及归属地探秘
背景:博主本是一位Windows桌面应用程序开发工程师,对网络通信一知半解.一日老婆逛完某宝,问:"为什么他们知道我的地址呢,他们是怎么获取我的地址的呢?" 顺着这个问题我们的探秘 ...
- windows下获取IP地址的两种方法
windows下获取IP地址的两种方法: 一种可以获取IPv4和IPv6,但是需要WSAStartup: 一种只能取到IPv4,但是不需要WSAStartup: 如下: 方法一:(可以获取IPv4和I ...
- 【Win 10 应用开发】获取本机的IP地址
按照老规矩,也是朋友的建议,老周今天在吹牛之前,先讲一个小故事. 有朋友问我,老周,你现在还发短信吗,你每个月用多少电话费?唉,实话说,现在真的发短信不多了,套餐送的130条短信,每月都发不了一条.至 ...
- 计算机网络学习笔记--网络层之IP地址与子网
IPv4地址: 我们知道在网络层(TCP/IP体系结构的网际互联层),最重要的一个协议就是IP协议,现在正处于IPv4和IPv6的过渡时期,但目前来说,IPv4仍为主流,所以主要讲Ipv4. IP地址 ...
随机推荐
- 有点儿神奇,原来vue3的setup语法糖中组件无需注册因为这个
前言 众所周知,在vue2的时候使用一个vue组件要么全局注册,要么局部注册.但是在setup语法糖中直接将组件import导入无需注册就可以使用,你知道这是为什么呢?注:本文中使用的vue版本为3. ...
- Pycharm创建的虚拟环境,使用命令行指定库的版本进行安装
Pycharm创建的项目,使用了虚拟环境,对库的版本进行管理:有些项目的对第三方库的版本 要求不同,可使用虚拟环境进行管理 直接想通过pip命令安装,直接看第3点 操作步骤: 1.找到当前项目的虚拟环 ...
- 🚀 Karpor - 让 AI 全面赋能 Kubernetes!
什么是 Karpor? 一言以蔽之,Karpor 是一个现代化的 Kubernetes 可视化工具,核心特性聚焦在 搜索. 洞察. AI ,目标是更方便快捷地连接平台和多集群,并用 AI 赋能 Ku ...
- Java基础:线程的三种创建方式
一.继承Thread类 定义一个类继承线程类Thread 重写run()方法 创建线程对象 调用线程对象的start()方法创建线程 Thread类的常用API setName(String name ...
- debian12 创建本地harbor镜像库
前言 harbor是一个docker/podman镜像管理库,可用于存储私人镜像.现将本人在debian12系统搭建harbor镜像库的过程记录下来,留作后续参考. 可以参考github harbor ...
- Java常见问题-基础
JDK版本新特性: JDK1.4 正则表达式,异常链,NIO,日志类,XML解析器,XLST转换器 JDK1.5 自动装箱.泛型.动态注解.枚举.可变长参数.遍历循环 JDK1.6 提供动态语言支持. ...
- Vue 框架怎么实现对象和数组的监听?
如果被问到 Vue 怎么实现数据双向绑定,大家肯定都会回答 通过 Object.defineProperty() 对数据进行劫持,但是 Object.defineProperty() 只能对属性进行数 ...
- 【RocketMQ 系列】 RocketMQ 双主双从(同步双写) 集群搭建
1. 各角色介绍 Producer:消息的发送者:举例:发信者 Consumer:消息接收者:举例:收信者 Broker:暂存和传输信息:举例:邮局 NameServer:管理Broker:举例:各个 ...
- Unity无法安装Entities 1.2.0 Package的解决方法
会出现如下的错误提示: 本质原因是国内版的Unity使用了自己的Package加速CDN:packages.unity.cn,而不是官方的packages.unity.com.而这个CDN更新了Ent ...
- 操作系统|SPOOLing(假脱机)技术
什么是假脱机技术,它可以解决什么问题? 什么是脱机技术 要回答什么是假脱机技术,首先我们需要知道什么是脱机技术.<计算机操作系统(第四版)>写道: 为了解决人机矛盾及CPU和I/O设备之间 ...