题目地址: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. u-boot(六)小结

    目录 u-boot(六)小结 概述 内存分布 内核交互参数 title: u-boot(六)小结 tags: linux date: 2018-09-27 23:23:05 --- u-boot(六) ...

  2. TF的使用

      激活函数 关于激活函数的介绍请参考:激活函数 这里只是记录TF提供的激活函数 import tensorflow as tf a = tf.nn.relu( tf.matmul(x, w1) + ...

  3. centos 7 上zabbix 3.0 服务端安装

    zabbix服务端安装 安装完毕mysql-5.6.php5.6 mysql-5.6安装:https://www.cnblogs.com/xzlive/p/9771642.html  创建zabbix ...

  4. SpringBoot笔记十一:html通过Ajax获取后端数据

    我们知道在Java Web中,前端的JSP可以使用EL表达式来获取Servlet传过来的数据Spring Boot中也有Thymeleaf模板可以使用th: text="${XXX}&quo ...

  5. expdp和impdp导入导出用法【转】

    关于expdp和impdp exp和imp是客户端工具程序,它们既可以在客户端使用,也可以在服务端使用.expdp和impdp是服务端的工具程序,他们只能在ORACLE服务端使用,不能在客户端使用.i ...

  6. 2018牛客网暑期ACM多校训练营(第九场)A -Circulant Matrix(FWT)

    分析 大佬说看样例就像和卷积有关. 把题目化简成a*x=b,这是个xor的FWT. FWT的讲解请看:https://www.cnblogs.com/cjyyb/p/9065615.html 那么要求 ...

  7. 如何在Mac上搭建自己的服务器——Nginx

    1.安装Homebrew 打开终端,输入: ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/ ...

  8. vue基于组件实现简单的todolist

    把todolist拆分为header.footer.list三个模块 index文件 <!DOCTYPE html> <html lang="en"> &l ...

  9. vue watch bug记录

    watch中,写箭头函数,获取不到正确的this 换成function,正确取到this

  10. mkdocs 生成帮助文档

    简介 MkDocs 可以同时编译多个markdown文件,形成书籍一样的文件.有多种主题供你选择,很适合项目使用. MkDocs 是快速,简单和华丽的静态网站生成器,可以构建项目文档.文档源文件在 M ...