题传

非常暴力的做法:每个点维护一颗平衡树,然后启发式合并。

但是这样的暴力做法会被回溯操作卡飞天。

先建出一棵操作树,将回溯操作简化为回退一次操作。

思考平衡树的劣势在哪里,合并和询问的复杂度极其不平衡。

想到平衡复杂度,那就只好分块了。

每个点维护一个值域分块,记录 \(cnt_{i, j}\) 为 \(i\) 点为根的子树中第 \(j\) 个值域块有多少个数。

连边和退边操作直接用可撤销并查集维护,合并/分裂时将两个根之间的贡献相加/减去。

询问时直接用根的 \(cnt\) 找到在哪个值域块,然后暴力遍历在找到值域块的所有数(包括不在当前树内的),注意离散化的时候不能去重,否则复杂度不正确。

那么你大概得到了一个时间 \(O(nB\log n)\),空间 \(O(\frac{n^2}{B})\) 的东西。

发现这题卡空间,把 \(B\) 调大一点即可。还是过不去的话,注意 \(\frac{n}{B}\) 不是很大,把 \(cnt\) 开 short 立省一半空间。

Code:

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <cctype>
#include <vector>
#include <queue>
#include <bitset>
#define vi vector<int>
#define pb push_back
#define mp make_pair
#define st first
#define nd second
using namespace std;
typedef long long ll;
typedef pair <int, int> Pii;
const int INF=0x3f3f3f3f;
const int cp=998244353;
inline int mod(int x){if(x>=cp) x-=cp;if(x<0) x+=cp;return x;}
inline void plust(int &x, int y){x=mod(x+y);return ;}
inline void minut(int &x, int y){x=mod(x-y);return ;}
inline int read(){
char ch=getchar();int x=0, f=1;
while(!isdigit(ch)){if(ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
inline void write(int x){
if(x<0) putchar('-'), x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
inline int ksm(int a, int b=cp-2){
int ret=1;
for(; b; b>>=1, a=1ll*a*a%cp)
if(b&1) ret=1ll*ret*a%cp;
return ret;
}
const int N=1e5+5;
const int M=4000;
int n, m, X[N], Y[N], bid[N], L[25], R[25], a[N], id[N], ans[N];
int siz[N], f[N], p[N], num[N], vis[N];
short cnt[N][25];
int find(int x){return (x^f[x])?find(f[x]):f[x];}
vi G[N], rub;
void calc(int x, int y, int op){for(int i=bid[1]; i<=bid[n]; ++i) cnt[x][i]+=op*cnt[y][i];}
bool merge(int x, int y){
int fx=find(x), fy=find(y);if(fx==fy) return 0;
if(siz[fx]<siz[fy]) swap(fx, fy);
rub.pb(fy);f[fy]=fx, siz[fx]+=siz[fy];
calc(fx, fy, 1);
return 1;
}
void reback(){
int fy=rub.back();rub.pop_back();
calc(f[fy], fy, -1), siz[f[fy]]-=siz[fy], f[fy]=fy;
}
int query(int x, int y){
x=find(x);
int i=0, s=0;for(; i<=bid[n]&&s+cnt[x][i]<y; ++i) s+=cnt[x][i];
if(i>bid[n]) return -1;
for(int j=L[i]; j<=R[i]; ++j) if(find(num[j])==x&&(++s)==y) return p[j];
return 0;
}
void dfs(int x){
bool flg=0;
if(Y[x]>0) flg=merge(X[x], Y[x]);
else if(Y[x]<0) ans[x]=query(X[x], -Y[x]);
for(auto v:G[x]) dfs(v);
if(flg) reback();
}
signed main(){
n=read(), m=read();
for(int i=1; i<=n; ++i)
a[i]=p[i]=read(), siz[f[i]=i]=1;
sort(p+1, p+n+1);
for(int i=1, pos; i<=n; ++i)
pos=lower_bound(p+1, p+n+1, a[i])-p,
a[i]=pos+(vis[pos]++), num[a[i]]=i;//, printf("%d ", a[i]);puts("");
for(int i=1, c=0; i<=n; i+=M, ++c)
for(int j=0; j<M&&i+j<=n; ++j)
L[c]=i, R[c]=i+j, bid[i+j]=c;
for(int i=1; i<=n; ++i) cnt[i][bid[a[i]]]=1;//printf("%d\n", bid[n]);
for(int i=1, pos=0; i<=m; ++i){
int op=read();
if(op==1) X[i]=read(), Y[i]=read(), G[pos].pb(i), pos=id[i]=i;
if(op==2) pos=id[i]=id[read()];
if(op==3) X[i]=read(), Y[i]=-read(), G[pos].pb(i), pos=id[i]=i;
}
dfs(0);
for(int i=1; i<=m; ++i) if(Y[i]<0) printf("%d\n", ans[i]);
return 0;
}

[Ynoi2014] 等这场战争结束之后的更多相关文章

  1. [Ynoi2014]不归之人与望眼欲穿的人们

    题目大意: 给定一个序列,每次单点修改一个数,或给定$x$,询问最短的or起来大于等于$x$的区间的长度(不存在输出-1). 解题思路: 在太阳西斜的这个世界里,置身天上之森.等这场战争结束之后,不归 ...

  2. [Ynoi2015]即便看不到未来

    题目大意: 给定一个序列,每次询问,给出一个区间$[l,r]$. 设将区间内的元素去重后重排的数组为$p$,求$p$中长度为$1\sim 10$的极长值域连续段个数. 长度为$L$的极长值域连续段的定 ...

  3. [Ynoi2015]纵使日薄西山

    题目大意: 给定一个序列,每次单点修改,然后进行询问. 定义一次操作为,选择一个位置$x$,将这个位置的数和左边.右边两个位置的数(不存在则忽略)各减去1,然后和0取max. 对序列中最大的位置进行一 ...

  4. [Ynoi2015]盼君勿忘

    题目大意: 给定一个序列,每次查询一个区间\([l,r]\)中所有子序列分别去重后的和\(\bmod p\)(每次询问模数不同). 解题思路: 在太阳西斜的这个世界里,置身天上之森.等这场战争结束之后 ...

  5. [Ynoi2015]我回来了

    题目大意: 给定一张无向无权图,每次给定若干个二元组\((x_i,y_i)\),定义点\(u\)满足条件,当且仅当存在\(i\),并满足\(dist(u,x_i)\leqslant y_i\)(\(d ...

  6. [Ynoi2015]此时此刻的光辉

    题目大意: 给定一个序列,每次询问一段区间的数的乘积的约数个数. 解题思路: 在太阳西斜的这个世界里,置身天上之森.等这场战争结束之后,不归之人与望眼欲穿的众人, 人人本着正义之名,长存不灭的过去.逐 ...

  7. 题解 P5072 【[Ynoi2015] 盼君勿忘】

    在太阳西斜的这个世界里,置身天上之森.等这场战争结束之后,不归之人与望眼欲穿的众人, 人人本着正义之名,长存不灭的过去.逐渐消逝的未来.我回来了,纵使日薄西山,即便看不到未来,此时此刻的光辉,盼君勿忘 ...

  8. 东大OJ-1544: GG的战争法则

    题目描述 你在桥上看风景 看风景的人在楼上看你 明月装饰了你的窗子 你装饰了我的梦 这是GG在长坂坡发出的感叹. 三年前GG莫名的穿越到了三国时期,在这三年里他看尽了各种杀戮,心里早已麻木.GG他渴望 ...

  9. BAT线下战争:巨额投资或培养出自己最大对手(包括美团、58、饿了么在内的公司都在计划推出自己的支付工具和金融产品,腾讯只做2不做O)

    BAT线下战争:巨额投资或培养出自己最大对手 2015年10月12日09:49   <财经>杂志    我有话说(18人参与) 收藏本文        BAT大举投资线下公司,看似咄咄逼人 ...

  10. Swift开发小技巧--自定义转场动画

    自定义转场动画 个人理解为重写了被弹出控制器的modal样式,根据自己的样式来显示modal出来的控制器 例:presentViewController(aVC, animated: true, co ...

随机推荐

  1. 【Windows】如何关闭Windows10、Windows11自动更新

    如何关闭Windows10自动更新 零.问题 Windows10老是自动更新,有时候第二天起来又得重新打开软件,真麻烦,Win10自动更新的时候还有点卡. 如何关闭? 经过上网查询,发现完全关闭难度比 ...

  2. 从零开始构建智能聊天机器人:Rasa与ChatGPT API实战教程

    引言:AI对话系统的时代机遇 在数字化转型浪潮中,聊天机器人已成为连接用户与服务的关键纽带.无论是客服系统中的7×24小时即时响应,还是智能家居中的语音交互,聊天机器人正在重塑人机交互方式.本文将通过 ...

  3. firebase studio硬刚cursor,送免费云服务可跑23b大模型

    谷歌IDX提供免费高配云服务器(16核CPU,64G内存,300G硬盘),无需绑卡,只需一个能正常使用的谷歌账号.这是一个非常强大的开发环境,特别适合运行大型AI模型和开发工作. 一.Google I ...

  4. 可轻便docker部署的密码保存系统:Vaultwarden

    一.简介 Vaultwarden是著名的Bitwarden项目的一个分支,是一个社区驱动的项目,使用Rust语言编写.它是Bitwarden的轻量级自托管替代方案,完全兼容Bitwarden客户端协议 ...

  5. Java--事务,操作数据库,实现转账

    更新:2019/3/29 目录 简介 事务的四个特性 一个小Demo 目录结构 jdbc.properties JDBCUtil.java TestTransaction.java[核心代码] 数据库 ...

  6. 代码随想录第十六天 | Leecode 513. 找树左下角的值、112. 路径总和、113. 路径总和 II、106. 从中序与后序遍历序列构造二叉树

    Leecode 513. 找树左下角的值 题目描述 给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值. 假设二叉树中至少有一个节点. 示例 1: 输入: root = [ ...

  7. 【安装】Linux下安装CUDA ToolKit 11.4和cuDNN 8

    注意!如果你使用的是pytorch,只需要装好CUDA,不需要装cuDNN.而且完全可以等到报错了再装CUDA,一般情况系统都已经装好CUDA Toolkit了. 除非你只装了低版本的CUDA Too ...

  8. MNIST实例-Tensorflow 初体验

    目的还是熟悉这种 tensorflow 框架的基本流程, 即如何导包, 反正我神经网络相关的一些经典理论, BP推导呀, 卷积神经网络呀, 递归神经网络这些的数学原理, 我已经基本推导一遍了, 已基本 ...

  9. C# 从PDF文档中提取图片

    当 PDF 文件中包含有价值的图片,如艺术画作.设计素材.报告图表等,提取图片可以将这些图像资源进行单独保存,方便后续在不同的项目中使用,避免每次都要从 PDF 中查找.本文将介绍如何使用C#通过代码 ...

  10. Java和C++性能大比拼

    用来运行Java语言的HotSpot VM主要是用C++语言来写的,所以我们在研究JDK时不得不去学习C++这门语言.C++和Java都是面向对象的语言,所以它们常被拿来做比较.本文将从性能的角度对比 ...