• 传送门
  • 题面
  • 思路
  • 参考代码
  • 参考代码(数组版)
  • 总结
传送门
题面

强制在线。

思路

首先,很明显这是一道数据结构裸题,总结一下,需要维护这样一个基于多个可重集的 ADT:

  1. 向某个集合中插入元素。
  2. 向某个集合中删除元素。
  3. 合并两个集合,并清空其中一个集合。
  4. 求集合元素的 mex。
  5. 求最小不能用子集和表示的正整数,我们就照着题解叫它神秘数吧。
  6. 可持久化。

最重要的一点是,这道题只要求部分可持久化

考试的时候,我假设我会我知道的所有东西,可是还是做不来,唉,我太弱啦!我想到的是可持久化 Treap,但是这样无法完成操作 3 和 操作 5。

正解是可持久化权值线段树。权值线段树也是可以求 mex 的,可是我却没有想到,唉,我太弱啦!线段树的存在使得合并操作能够使用线段树合并(虽然我还没有写过可持久化线段树的合并),而神秘数的求法还需要进行一定分析。

考虑增量构造。设当前的神秘数为 xxx,x" role="presentation" style="position: relative;">xxx 一开始为 000。显然我们是可以表示 1∼x−1" role="presentation" style="position: relative;">1∼x−11∼x−11 \sim x - 1 中的所有数的。考虑加入一个数后会发生什么,设加入的数为 yyy。若 x&lt;y" role="presentation" style="position: relative;">x<yx<yx ,显然 yyy 是没有用的。若 y≤x" role="presentation" style="position: relative;">y≤xy≤xy \le x,那么我们可以用它表示 1∼x+y−11∼x+y−11 \sim x + y - 1 中的所有数,新的 xxx 为 x+y" role="presentation" style="position: relative;">x+yx+yx + y。

求神秘数时,自然不能乱序求。我们将可重集中的所有数按从小到大的顺序排序后处理,这样就不会出错了。一旦某个数大于了当前的 xxx,那么就可以退出处理了,因为后面的数更大。

现在我们得到了 O(n)" role="presentation" style="position: relative;">O(n)O(n)O(n) 求神秘数的方法,我们能把它放到线段树上吗?需要注意的是,我们不能像找 mex 一样直接在线段树上二分,因为有可能左边一整段满足和大于当前数,但是左边的里面存在一段和小于当前数的,就不满足条件了。一言以蔽之,神秘数不能二分,那怎么做呢?

考虑暴力。一开始,让神秘数等于 x=1x=1x = 1,然后我们查找小于等于 xxx 的数的和,显然 1" role="presentation" style="position: relative;">111 到这个和中的所有数都是可以表示出来的,所以我们令 xxx 等于这个和加 1" role="presentation" style="position: relative;">111。如果这样操作后 xxx 不改变,那么我们就退出。发现,如果我们没有退出,那我们一定加上了至少一个比上一次操作前的 x" role="presentation" style="position: relative;">xxx 大的数,换句话说,我们通过两次操作,能够保证 xxx 至少翻一倍,因此时间复杂度是 O(log⁡n)" role="presentation" style="position: relative;">O(logn)O(log⁡n)O(\log n) 的,总的时间复杂度为 O(nlog2n)O(nlog2⁡n)O(n \log^2 n)。

所以这道题就解决了:用可持久化线段树维护线段内权值之和以及权值种类之和,二分求 mex,暴力求神秘数。

参考代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef LL INT_PUT;
INT_PUT readIn()
{INT_PUT a = 0; bool positive = true;char ch = getchar();while (!(ch == '-' || std::isdigit(ch))) ch = getchar();if (ch == '-') { positive = false; ch = getchar(); }while (std::isdigit(ch)) { a = a * 10 - (ch - '0'); ch = getchar(); }return positive ? -a : a;
}
void printOut(INT_PUT x)
{char buffer[20]; int length = 0;if (x < 0) putchar('-'); else x = -x;do buffer[length++] = -(x % 10) + '0'; while (x /= 10);do putchar(buffer[--length]); while (length);
}
const int maxr = int(5e5);
const int maxn = int(5e5) + 5;
int lastans;
int n, q;int decode(int val)
{return (val + lastans - 1) % n + 1;
}class FuncSegTree
{struct Node{LL tsum;int sum;Node* ch[2];Node() : tsum(), sum() {}};static Node* null;static struct Initializer{Initializer(){null = new Node;null->ch[0] = null->ch[1] = null;}} initializer;static int g_L, g_R, g_Pos, g_Val;static Node* alloc(){Node* ret = new Node;ret->ch[0] = ret->ch[1] = null;return ret;}static Node* alloc(const Node* t){return new Node(*t);}struct Version{Node* entry;int t;Version() {}Version(Node* e, int t) : entry(e), t(t) {}};std::vector<Version> ver;int add(Node* &node, int l, int r, Node* src){if (!(l <= g_Pos && g_Pos <= r)){node = src;return l == r ? (bool)node->sum : node->sum;}node = alloc();if (l == r){node->tsum = (LL)l * (node->sum = std::max(0, src->sum + g_Val));return (bool)node->sum;}int mid = (l + r) >> 1;int ret = 0;ret += add(node->ch[0], l, mid, src->ch[0]);ret += add(node->ch[1], mid + 1, r, src->ch[1]);node->sum = ret;node->tsum = node->ch[0]->tsum + node->ch[1]->tsum;return ret;}static int merge(Node* &des, Node* src, int l, int r){if (src == null) return l == r ? (bool)des->sum : des->sum;if (des == null){des = src;return l == r ? (bool)des->sum : des->sum;}des = alloc(des); // 可持久化:要修改时先新建结点。不用担心修改了父子关系,因为它的父亲也是新的。des->sum += src->sum;des->tsum += src->tsum;if (l == r){return (bool)des->sum;}int mid = (l + r) >> 1;int ret = 0;ret += merge(des->ch[0], src->ch[0], l, mid);ret += merge(des->ch[1], src->ch[1], mid + 1, r);return des->sum = ret;}int mex(Node* cnt){int l = 1;int r = maxr;int k = 0;while (r - l > 0){int mid = (l + r) >> 1;if (k + cnt->ch[0]->sum >= mid){k += cnt->ch[0]->sum;l = mid + 1;cnt = cnt->ch[1];}else{r = mid;cnt = cnt->ch[0];}}return l;}LL query(Node* node, int l, int r){if (g_L <= l && r <= g_R){return node->tsum;}int mid = (l + r) >> 1;LL ret = 0;if (g_L <= mid) ret += query(node->ch[0], l, mid);if (g_R > mid) ret += query(node->ch[1], mid + 1, r);return ret;}LL query(int l, int r, Node* root){g_L = l;g_R = r;return query(root, 1, maxr);}LL smex(Node* cnt){LL ret = 1;while (ret <= maxr){LL t = query(1, ret, cnt);if (ret == t + 1) break;if (t > maxr)t = query(1, maxr, cnt);ret = t + 1;}return ret;}void wander(Node *node, int l, int r){if (node == null) return;if (l == r){for (int i = 1; i <= node->sum; i++){printOut(l);putchar(' ');}return;}int mid = (l + r) >> 1;wander(node->ch[0], l, mid);wander(node->ch[1], mid + 1, r);}public:FuncSegTree() : ver(1, Version(null, 0)) {}void clone(int t){ver.push_back(Version(ver.back().entry, t));}void add(int pos, int val){g_Pos = pos;g_Val = val;add(ver.back().entry, 1, maxr, ver.back().entry);}static void merge(FuncSegTree& des, FuncSegTree& src, int t){des.clone(t);src.clone(t);merge(des.ver.back().entry, src.ver.back().entry, 1, maxr);src.ver.back().entry = null; // 不操作 src 内部,直接最后赋值为 null}Node* get(int t){int l = 0;int r = ver.size();while (r - l > 1){int mid = (l + r) >> 1;if (ver[mid].t <= t)l = mid;elser = mid;}return ver[l].entry;}int mex(){return mex(ver.back().entry);}int mex(int t){return mex(get(t));}LL smex(){return smex(ver.back().entry);}LL smex(int t){return smex(get(t));}void wander(){wander(ver.back().entry, 1, maxr);}
};
FuncSegTree::Node* FuncSegTree::null;
FuncSegTree::Initializer FuncSegTree::initializer;
int FuncSegTree::g_L, FuncSegTree::g_R, FuncSegTree::g_Pos, FuncSegTree::g_Val;std::vector<FuncSegTree> st;void run()
{n = readIn();st.resize(n + 1);q = readIn();for (int i = 1; i <= q; i++){int type = readIn();if (type == 1){int u = decode(readIn());int g = readIn();st[u].clone(i);st[u].add(g, 1);}else if (type == 2){int u = decode(readIn());int g = readIn();st[u].clone(i);st[u].add(g, -1);}else if (type == 3){int u = decode(readIn());int v = decode(readIn());if (u == v) continue;FuncSegTree::merge(st[u], st[v], i);}else if (type == 4){int u = decode(readIn());printOut(lastans = st[u].mex());putchar(' ');printOut(st[u].smex());putchar('\n');}else if (type == 5){int u = decode(readIn());int t = readIn();printOut(lastans = st[u].mex(t - 1));putchar(' ');printOut(st[u].smex(t - 1));putchar('\n');}}for (int i = 1; i <= n; i++){st[i].wander();putchar('0');putchar('\n');}
}int main()
{
#ifndef LOCALfreopen("forgive.in", "r", stdin);freopen("forgive.out", "w", stdout);
#endifrun();return 0;
}

代码体验不错,就是读题的时候没有发现可以在没有种子的时候拿走种子,这个时候什么都不用做。最后直接 959595 分,有一个点超内存了,要 750 MiB 左右。我最讨厌卡指针内存了,唉,我太弱啦!

注意可持久化线段树的合并:这个操作不是完全可持久化的。实现时,对于 des 的所有被改了的结点都要新建,对于 src 什么都不管,直接把根结点设成 null。考试时应该是可以 yy 出来的,毕竟这个就是我 yy 出来的。

参考代码(数组版)

改过来了,跑得飞快。

注意空间要开 303030 倍,可能是因为我太弱了,可持久化线段树的空间要求太高了。唉,我太弱啦!

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef LL INT_PUT;
INT_PUT readIn()
{INT_PUT a = 0; bool positive = true;char ch = getchar();while (!(ch == '-' || std::isdigit(ch))) ch = getchar();if (ch == '-') { positive = false; ch = getchar(); }while (std::isdigit(ch)) { a = a * 10 - (ch - '0'); ch = getchar(); }return positive ? -a : a;
}
void printOut(INT_PUT x)
{char buffer[20]; int length = 0;if (x < 0) putchar('-'); else x = -x;do buffer[length++] = -(x % 10) + '0'; while (x /= 10);do putchar(buffer[--length]); while (length);
}
const int maxr = int(5e5);
const int maxn = int(5e5) + 5;
int lastans;
int n, q;int decode(int val)
{return (val + lastans - 1) % n + 1;
}class FuncSegTree
{struct Node{LL tsum;int sum;int ch[2];Node() : tsum(), sum(), ch() {}};static Node p[15000000];static int g_L, g_R, g_Pos, g_Val;static int counter;static int alloc(){return ++counter;}static int alloc(const int t){++counter;p[counter] = p[t];return counter;}struct Version{int entry;int t;Version() {}Version(int e, int t) : entry(e), t(t) {}};std::vector<Version> ver;int add(int& node, int l, int r, int src){if (!(l <= g_Pos && g_Pos <= r)){node = src;return l == r ? (bool)p[node].sum : p[node].sum;}node = alloc();if (l == r){p[node].tsum = (LL)l * (p[node].sum = std::max(0, p[src].sum + g_Val));return (bool)p[node].sum;}int mid = (l + r) >> 1;int ret = 0;ret += add(p[node].ch[0], l, mid, p[src].ch[0]);ret += add(p[node].ch[1], mid + 1, r, p[src].ch[1]);p[node].sum = ret;p[node].tsum = p[p[node].ch[0]].tsum + p[p[node].ch[1]].tsum;return ret;}static int merge(int &des, int src, int l, int r){if (src == 0) return l == r ? (bool)p[des].sum : p[des].sum;if (des == 0){des = src;return l == r ? (bool)p[des].sum : p[des].sum;}des = alloc(des); // 可持久化:要修改时先新建结点。不用担心修改了父子关系,因为它的父亲也是新的。p[des].sum += p[src].sum;p[des].tsum += p[src].tsum;if (l == r){return (bool)p[des].sum;}int mid = (l + r) >> 1;int ret = 0;ret += merge(p[des].ch[0], p[src].ch[0], l, mid);ret += merge(p[des].ch[1], p[src].ch[1], mid + 1, r);return p[des].sum = ret;}int mex_(int cnt){int l = 1;int r = maxr;int k = 0;while (r - l > 0){int mid = (l + r) >> 1;if (k + p[p[cnt].ch[0]].sum >= mid){k += p[p[cnt].ch[0]].sum;l = mid + 1;cnt = p[cnt].ch[1];}else{r = mid;cnt = p[cnt].ch[0];}}return l;}LL query_(int node, int l, int r){if (g_L <= l && r <= g_R){return p[node].tsum;}int mid = (l + r) >> 1;LL ret = 0;if (g_L <= mid) ret += query_(p[node].ch[0], l, mid);if (g_R > mid) ret += query_(p[node].ch[1], mid + 1, r);return ret;}LL query(int l, int r, int root){g_L = l;g_R = r;return query_(root, 1, maxr);}LL smex_(int cnt){LL ret = 1;while (ret <= maxr){LL t = query(1, ret, cnt);if (ret == t + 1) break;if (t > maxr)t = query(1, maxr, cnt);ret = t + 1;}return ret;}void wander(int node, int l, int r){if (node == 0) return;if (l == r){for (int i = 1; i <= p[node].sum; i++){printOut(l);putchar(' ');}return;}int mid = (l + r) >> 1;wander(p[node].ch[0], l, mid);wander(p[node].ch[1], mid + 1, r);}public:FuncSegTree() : ver(1, Version(0, 0)) {}void clone(int t){ver.push_back(Version(ver.back().entry, t));}void add(int pos, int val){g_Pos = pos;g_Val = val;add(ver.back().entry, 1, maxr, ver.back().entry);}static void merge(FuncSegTree& des, FuncSegTree& src, int t){des.clone(t);src.clone(t);merge(des.ver.back().entry, src.ver.back().entry, 1, maxr);src.ver.back().entry = 0; // 不操作 src 内部,直接最后赋值为 0}int get(int t){int l = 0;int r = ver.size();while (r - l > 1){int mid = (l + r) >> 1;if (ver[mid].t <= t)l = mid;elser = mid;}return ver[l].entry;}int mex(){return mex_(ver.back().entry);}int mex(int t){return mex_(get(t));}LL smex(){return smex_(ver.back().entry);}LL smex(int t){return smex_(get(t));}void wander(){wander(ver.back().entry, 1, maxr);}friend void run();
};
int FuncSegTree::g_L, FuncSegTree::g_R, FuncSegTree::g_Pos, FuncSegTree::g_Val;
FuncSegTree::Node FuncSegTree::p[15000000];
int FuncSegTree::counter;std::vector<FuncSegTree> st;void run()
{n = readIn();st.resize(n + 1);q = readIn();for (int i = 1; i <= q; i++){int type = readIn();if (type == 1){int u = decode(readIn());int g = readIn();st[u].clone(i);st[u].add(g, 1);}else if (type == 2){int u = decode(readIn());int g = readIn();st[u].clone(i);st[u].add(g, -1);}else if (type == 3){int u = decode(readIn());int v = decode(readIn());if (u == v) continue;FuncSegTree::merge(st[u], st[v], i);}else if (type == 4){int u = decode(readIn());printOut(lastans = st[u].mex());putchar(' ');printOut(st[u].smex());putchar('\n');}else if (type == 5){int u = decode(readIn());int t = readIn();printOut(lastans = st[u].mex(t - 1));putchar(' ');printOut(st[u].smex(t - 1));putchar('\n');}}for (int i = 1; i <= n; i++){st[i].wander();putchar('0');putchar('\n');}
}int main()
{
#ifndef LOCALfreopen("forgive.in", "r", stdin);freopen("forgive.out", "w", stdout);
#endifrun();return 0;
}
总结

主要还是没有想到那个神秘数怎么求,另外也没有往线段树上想,所以才酿成爆零的悲剧。唉,我太弱啦!

JZOJ 5750 青青草原播种计划相关推荐

  1. 懒羊羊吃青草:懒羊羊是一只非常能吃的羊,它在青青草原上发现一块巨大的正方形草地,但灰太狼已经抢先一步在草地上布下了若干陷阱。正方形草地位于直角坐标系中...

    题面描述 懒羊羊是一只非常能吃的羊,它在青青草原上发现一块巨大的正方形草地,但灰太狼已经抢先一步在草地上布下了若干陷阱.正方形草地位于直角坐标系中,左下角坐标为 (1, 1) ,右上角坐标为 (m, ...

  2. c语言喜羊羊,青青草原101C位之争,喜羊羊vs懒羊羊

    原标题:青青草原101C位之争,喜羊羊vs懒羊羊 还记得小时候陪伴你长大的动画片<喜羊羊与灰太狼>吗? 羊历3513年,青青草原上,羊羊族群已经十分兴旺发达.在羊羊一族里面已经有小镇,有学 ...

  3. 用Python防止头上一片青青草原

    1.使用pynput库     pynput 可以监控我们的键盘和鼠标.目前具有此类功能的库有很多,比如 pygame 等游戏库,但是当我们只需要监控键盘和鼠标时,它们就显得过于笨重了. 2.对键盘监 ...

  4. 【JZOJ A组】斩杀计划

    Description 众所周知,小J和小G是死对头,一天小G带领一群小弟找到了小J. 问题描述 小G有n个小弟,第i个小弟有ai点攻击力,小G有m点血量. 小J在小G找小第的时间里去找小Z学到了膜法 ...

  5. 播种:人生第一篇博客

    播种:人生第一篇博客 前言 一.自我介绍 二.未来小目标 三.学习编程计划 四.致自己 五.结语 前言 此篇为开始的见证,谨记自己的初心 自我介绍 高考结束后再三考虑还是加入这个很有"前景& ...

  6. 第十五届全国大学生智能车人工智能创意组复赛首批团队名单

    第十五届全国大学生智能车人工智能创意组复赛首批团队名单 第十五届全国大学生智能车人工智能创意组比赛第一阶段结束了.经过组委会审核.参赛团队申诉后.首批入围创意组复赛的85支团队名单公布如下: 序号 团 ...

  7. 渔村小厂,如何成长为5G霸王?

    作者 | 贾凯强 2019,5G 的春风吹的正劲,吹到了紫禁城中,将故宫博物院吹成一座集数字化.信息化.智慧化于一身的"5G 智慧故宫". 紫禁城的朱墙,矗立了 600 年后,被 ...

  8. paradox 修改字段长度_关于生日的作文500字9篇

    关于生日的作文500字9篇 在日常的学习.工作.生活中,大家都写过作文,肯定对各类作文都很熟悉吧,借助作文人们可以反映客观事物.表达思想感情.传递知识信息.写起作文来就毫无头绪?下面是小编整理的生日的 ...

  9. 谨以此文纪念我的大学四年

      今天是2020年6月29日.算起来,我已经在家呆了整整半年了.如果没有疫情的影响,或许我已经顺利拿到双证,开始工作了.此时已是深夜,看着窗外车辆快速驶过的身影,纷飞的思绪,不禁回到四年前. 从高考 ...

  10. 自创文字小游戏《人类末日·丧尸危机》

    这个游戏有很多坑哦,其他就没什么要注意的啦. 你不要以为你以为的就是对的! 有些关卡不能通关纯属运气!!!最多10次就可以通关啦!!! 代码在哪里? #include <bits/stdc++. ...

最新文章

  1. 电脑蓝屏问题检查、解决、
  2. SQL --分支取数据
  3. JDBC连接MySQL数据库及示例
  4. Hibernate注解方式实现1-1双向关联
  5. __attribute__((unused)):可能不会用到,消除编译警告
  6. openoffice将html转成pdf,通过openOffice将office文件转成pdf
  7. 【DP】【单调队列】【NOI2005】瑰丽华尔兹
  8. warning: refname ‘HEAD‘ is ambiguous解决方法
  9. ASP.NET 性能监控工具和优化技巧
  10. 两种方式(xml+代码)构建SqlSessionFactory+完整实现
  11. 小莫微信影视机器人-自定义对接影视教程
  12. java修改jpg图片、mp3音频文件的后缀名
  13. 微软苏州校招笔试 12月27日
  14. 基于R语言进行K折交叉验证
  15. PDF压缩的使用工具有哪些
  16. Matlab2019 slrt(XPC)目标机U盘启动
  17. 使用IDAPython dump 内存
  18. Dropbox一款很实用的个人文件同步利器
  19. 雪亮工程、平安城市以及天网工程这三者有什么区别?
  20. perl Carp模块使用举例(转)

热门文章

  1. python-基础杂集
  2. php通过函数怎么禁止百度蜘蛛抓取,怎么屏蔽百度蜘蛛(Baiduspider)抓取网站
  3. pc游戏平台_如何提高您在PC游戏中的目标
  4. 如何将网页转为html文件,Chrome怎样保存网页为mhtml格式
  5. HTML---表格table标签中thead、tbody、tfoot的作用
  6. Linux网络编程-UDP实现QQ聊天功能
  7. 已有项目如何添加到gitee(码云)仓库
  8. 计算机硬件系统主机主要包括,组成计算机硬件系统的基本部分是什么?
  9. Massive MIMO
  10. python excel转csv日期变数字_将Excel转换为CSV正确转换日期字段