题目地址:P5270 无论怎样神树大人都会删库跑路

第一眼看上去是模拟,似乎是 \(O(n)\) 的

水题

信心满满的写完:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 6;
int n, T, Q, m, c[N], r[N], mx, cnt, d[N], ans, s[N], now;
vector<int> e[N];
queue<int> q;

int main() {
    cin >> n >> T >> Q;
    for (int i = 1; i <= T; i++) {
        int x;
        scanf("%d", &x);
        mx = max(mx, x);
        ++c[x];
    }
    for (int i = 1; i <= n; i++) {
        int len;
        scanf("%d", &len);
        while (len--) {
            int x;
            scanf("%d", &x);
            mx = max(mx, x);
            e[i].push_back(x);
        }
    }
    cin >> m;
    for (int i = 1; i <= m; i++) scanf("%d", &r[i]);
    for (int x = 0; x <= mx; x++) if (c[x] == d[x]) ++cnt;
    for (int i = 1; i <= m && i <= Q; i++) {
        for (unsigned int j = 0; j < e[r[i]].size(); j++) {
            int x = e[r[i]][j];
            q.push(x);
            if (d[x] == c[x]) --cnt;
            ++d[x];
            if (d[x] == c[x]) ++cnt;
        }
        while ((int)q.size() > T) {
            int x = q.front();
            q.pop();
            if (d[x] == c[x]) --cnt;
            --d[x];
            if (d[x] == c[x]) ++cnt;
        }
        if (cnt == mx + 1) ++ans;
    }
    if (Q <= m) {
        cout << ans << endl;
        return 0;
    }
    Q -= m;
    for (int i = 1; i <= m; i++) {
        for (unsigned int j = 0; j < e[r[i]].size(); j++) {
            int x = e[r[i]][j];
            q.push(x);
            if (d[x] == c[x]) --cnt;
            ++d[x];
            if (d[x] == c[x]) ++cnt;
        }
        while ((int)q.size() > T) {
            int x = q.front();
            q.pop();
            if (d[x] == c[x]) --cnt;
            --d[x];
            if (d[x] == c[x]) ++cnt;
        }
        if (cnt == mx + 1) ++now;
        s[i] = now;
    }
    cout << ans + now * (Q / m) + s[Q%m] << endl;
    return 0;
}

Wait!

Q:为什么在模拟完第一遍后要再模拟一遍呢?

A:第一遍初始时没有数,而后面会有剩下的数留到下一轮,因此第一遍对 \(ans\) 的贡献要特判QAQ

一遍AC

然而......

模拟是 \(O(n)\) 的?

抱歉,假了,50分滚粗

康康这组数据:

1 1e5 1e9
1 1 ... 1
1e5 1 1 ... 1
1e5
1 1 ... 1

你会发现

被卡成 \(O(n^2)\) 了!

Q:怎么办?

A:骂毒瘤出题人

我们发现瓶颈在这:

        for (unsigned int j = 0; j < e[r[i]].size(); j++) {
            int x = e[r[i]][j];
            q.push(x);
            if (d[x] == c[x]) --cnt;
            ++d[x];
            if (d[x] == c[x]) ++cnt;
        }
        while ((int)q.size() > T) {
            int x = q.front();
            q.pop();
            if (d[x] == c[x]) --cnt;
            --d[x];
            if (d[x] == c[x]) ++cnt;
        }

这个插入删除是 \(O(n)\) 的!

Q:我们能不能把它搞成 \(O(1)\) 的?

A:可以

考虑 Hash 思想

先看代码:

#include <bits/stdc++.h>
#define ull unsigned long long
#define pii pair<int, int>
using namespace std;
const int N = 1e5 + 6;
int n, T, Q, S[N], R[N], m, mx, len, ans, s[N], kkk;
vector<int> e[N];
struct H {
    ull h[6];
    H() {
        memset(h, 0, sizeof(h));
    }
} Hash[N], numS, now;
vector<H> num[N];
deque<pii> q;

inline ull RA() {
    return (ull)rand() * rand() * rand() * rand() * rand() * rand();
}

inline void add(H &a, H &b) {
    for (int i = 0; i < 6; i++) a.h[i] += b.h[i];
}

inline void del(H &a, H &b) {
    for (int i = 0; i < 6; i++) a.h[i] -= b.h[i];
}

inline bool equ(H a, H b) {
    for (int i = 0; i < 6; i++)
        if (a.h[i] != b.h[i]) return 0;
    return 1;
}

int main() {
    srand((unsigned)time(0));
    cin >> n >> T >> Q;
    for (int i = 1; i <= T; i++) {
        scanf("%d", &S[i]);
        mx = max(mx, S[i]);
    }
    for (int i = 1; i <= n; i++) {
        int len;
        scanf("%d", &len);
        while (len--) {
            int x;
            scanf("%d", &x);
            mx = max(mx, x);
            e[i].push_back(x);
        }
    }
    cin >> m;
    for (int i = 1; i <= m; i++) scanf("%d", &R[i]);
    for (int i = 0; i <= mx; i++)
        for (int j = 0; j < 6; j++)
            Hash[i].h[j] = RA();
    for (int i = 1; i <= T; i++) add(numS, Hash[S[i]]);
    for (int i = 1; i <= n; i++) {
        int len = e[i].size();
        num[i].resize(len);
        for (int j = len - 1; ~j; j--)
            add(num[i][j], Hash[e[i][j]]);
        for (int j = len - 2; ~j; j--)
            add(num[i][j], num[i][j+1]);
    }
    for (int i = 1; i <= m && i <= Q; i++) {
        add(now, num[R[i]][0]);
        len += e[R[i]].size();
        q.push_back({R[i], 0});
        while (len > T) {
            pii x = q.front();
            q.pop_front();
            del(now, num[x.first][x.second]);
            len -= e[x.first].size() - x.second;
            if (len < T) {
                int k = num[x.first].size() - (T - len);
                q.push_front({x.first, k});
                add(now, num[x.first][k]);
                len = T;
            }
        }
        if (equ(now, numS)) ++ans;
    }
    if (Q <= m) {
        cout << ans << endl;
        return 0;
    }
    Q -= m;
    for (int i = 1; i <= m && i <= Q; i++) {
        add(now, num[R[i]][0]);
        len += e[R[i]].size();
        q.push_back({R[i], 0});
        while (len > T) {
            pii x = q.front();
            q.pop_front();
            del(now, num[x.first][x.second]);
            len -= e[x.first].size() - x.second;
            if (len < T) {
                int k = num[x.first].size() - (T - len);
                q.push_front({x.first, k});
                add(now, num[x.first][k]);
                len = T;
            }
        }
        if (equ(now, numS)) ++kkk;
        s[i] = kkk;
    }
    cout << ans + kkk * (Q / m) + s[Q%m] << endl;
    return 0;
}

(小声)不好意思kkk,窝真的是没有变量名可用才用您的QWQ

我们给每个数若干维随机 Hash

    for (int i = 0; i <= mx; i++)
        for (int j = 0; j < 6; j++)
            Hash[i].h[j] = RA();

一串数的 Hash 值定义为每个数的每一维 Hash 分别相加(unsigned long long自然溢出)

如果两个串任意排列后可以相同,这两个串对应的每一维都应该相同

那么插入和删除就都可以做到 \(O(1)\) 了

你卡我随机化鸭

你卡我 Hash 鸭

你卡我常数大鸭

P5270 无论怎样神树大人都会删库跑路的更多相关文章

  1. Mysql binlog备份数据及恢复数据,学会这个,我在也不怕删库跑路啦~

    导读 我一直都主张,技多不压身(没有学不会的技术,只有不学习的人),多学一项技能,未来就少求人一次.网上经常听到xxx删库跑路,万一真的遇到了,相信通过今天的学习,也能将数据再恢复回来~~~ 当然啦, ...

  2. Oracle删库跑路

    --10g R2 startup mount exclusive restrict; alter system enable restricted session; drop database; -- ...

  3. SQL 从入门到 DBA 删库跑路

    SQL 从入门到 DBA 删库跑路 一.基础 人员信息表: ID 姓名 性别 出生 婚否 学历 工资 工会 35009449 孙xx 男 1978-2-17 未婚 中专 3000 TRUE 35000 ...

  4. 数据误操作,删库跑路?教你使用ApexSQLLog工具从 SQLServer日志恢复数据!

    前几天同事不小心误操作,将SQLServer库的一张表的一个状态字段给刷成了一个统一状态,由于是update执行所以原来的相关状态无法确定.发生这种事情的时候我的小伙伴背后 一凉,估计心里里面想这怕是 ...

  5. 教你如何6秒钟往MySQL插入100万条数据!然后删库跑路!

    教你如何6秒钟往MySQL插入100万条数据!然后删库跑路! 由于我用的mysql 8版本,所以增加了Timezone,然后就可以了 前提是要自己建好库和表. 数据库test, 表user, 三个字段 ...

  6. 【巨杉数据库SequoiaDB】巨杉Tech | “删库跑路”又出现,如何防范数据安全风险?

    最近,又双叕有企业被“删库”了.来自微盟官网的消息,微盟的业务系统数据库(包括主备)遭遇其公司运维人员的删除,系统将停止运营超48小时. 频发的类似事件也让大家对于数据安全的关注不断提高.数据是一个科 ...

  7. The Data Way Vol.3|做到最后只能删库跑路?DBA 能做的还有很多

    关于「The Data Way」 「The Data Way」是由 SphereEx 公司出品的一档播客节目.这里有开源.数据.技术的故事,同时我们关注开发者的工作日常,也讨论开发者的生活日常:我们聚 ...

  8. 因为它,我差点删库跑路:js防抖与节流

    前言 前端踩雷:短时间内重复提交导致数据重复. 对于前端大佬来说,防抖和节流的技术应用都是基本操作.对于"兼职"前端开发的来说,这些都是需要躺平的坑. 我们今天就来盘一盘js防抖与 ...

  9. 大数据开发从入门小白到删库跑路(一)- 获取Hadoop

    Hadoop是一个可以通过相对简单编程模型实现跨多台计算机集群分布式处理大型数据集的框架.它不是依赖于高额成本的硬件可靠性来提供高可用性,Hadoop的设计能从单个服务器扩展到数千台机器,每个机器提供 ...

随机推荐

  1. Grunt安装与环境配置

    公司项目还没有前后端分离,而前端是使用node.js搭建起来的,现在需要自己动手开发,故学习下并做为记录防止以后忘记. grunt依赖node.js,所以在安装之前确保你安装了 Node.js.然后开 ...

  2. 图形设计必备软件:CorelDRAW

    [CorelDRAW 激发创意] CorelDRAW Graphics Suite 是一款领先的图形设计软件,收到数百万专业人士.小型企业主以及全球设计爱好者的热捧.它可以提供无缝的图形.版面.插图. ...

  3. Go-day04

    今日概要: 1.内置函数.递归函数.闭包 2.数组与切片 3.map数据结构 4.package介绍 5.互斥锁和读写锁 一.内置函数 1.close:主要用来关闭channel 2.len:用来求长 ...

  4. 20165232 学习基础和c语言基础调查

    做中学读后感 学习是要思考的,仅仅实践是不够的: 不光会动手,还要理解背后的原理 不光会用工具,还要理解支撑的理论 技能是分层次的: 一项技能的掌握程度分为:新手/高级初学者/合格者/精通/专家 对技 ...

  5. Linux检查和收集硬件信息的常用命令总结

    Linux检查和收集硬件信息的常用命令总结 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. Linux基础真的很重要,基础不牢,地动山摇.这句话我是听老男孩创始人冉总说的一句话,起初 ...

  6. setAttribute和setParameter方法的区别

    getAttribute表示从request范围取得设置的属性,必须要先setAttribute设置属性,才能通过getAttribute来取得,设置与取得的为Object对象类型 getParame ...

  7. CSS3笔记3

    1.CSS的层叠性 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  8. 能使 Oracle 索引失效的六大限制条件【转】

    . 引用自: http://www.cnblogs.com/orientsun/archive/2012/07/05/2577351.html  总结得非常到位 Oracle 索引的目标是避免全表扫描 ...

  9. HDU 1026(迷宫 BFS+打印)

    题意是要穿过一个迷宫并且将每一步打印出来. 用宽搜的方法找到路径,在 vis 中存一下方向,只是这题被看到的一种不太对的运算符重载坑了很久...... 代码如下: #include <bits/ ...

  10. zookeeper的Java端API应用

    1. 基本使用 org.apache.zookeeper.Zookeeper是客户端入口主类,负责建立与server的会话.它提供了表1所示几类主要方法: 功能 描述 create 在本地目录树中创建 ...