P4119 [Ynoi2018] 未来日记 题解
最初分块,个人感觉如果之前做过带插入区间 \(\text{K}\) 小值和第二分块的话,应该还比较好想。
思路
由于要询问的是区间第 \(\text{k}\) 小值,自然而然的就可以想到分块套分块,外层对序列进行分块,内层运用值域分块,空间复杂度:\(O(n \sqrt n)\)。
具体的,我们对于序列上的每一个块维护两个数组。
第一个是每个数对于每个块的前缀和。
第二个是对第一个数组进行的分块。
所以对于一个区间的第 \(k\) 小,我们先将两边的散块暴力插入一个桶中,然后找到左右端点,依次判断每一个值域所分的块。
找到之后,再在块中扫一遍。
由于最多只有 \(\sqrt n\) 个块,每个块内也只有 \(\sqrt n\) 个数。
所以时间复杂度 \(O(\sqrt n)\)
我们现在来考虑如何实现修改:
把区间 \([l,r]\) 中所有 \(x\) 变成 \(y\)。
如果做过第二分块的话就可以想到用并查集处理这个问题。
对于散块,我们直接重构出整个块的并查集关系。
由于改变的只有 \(x\) 和 \(y\) 的个数,所以虽然其他的并查集关系有可能会改变,但在值域数组中的大小是不会改变的,所以只需要更改 \(x\) 和 \(y\) 的大小即可。
我们暴力统计更改后出这一块内 \(x\) 和 \(y\) 的个数,再暴力更改后面的每一个块,由于更改每一个块只需要 \(O(1)\)。
所以时间复杂度为 \(O(\sqrt n)\)。
至于整块,我们可以对于每一个块直接更改并查集,由于最多 \(\sqrt n\) 个块。
所以时间复杂度为 \(O(T\sqrt n)\),其中 \(T\) 为并查集所用时间。
总时间复杂度 \(O(nT\sqrt n)\)。
但这个代码还不足以通过此题,你只能获得 \(\text{64pts}\) 或者 \(\text{73pts}\)。
我们发现如果是左右端点不同块的修改,对于散块需要有两倍的常数,所以我们考虑对散块进行进一步的优化。
由于在散块内并查集更改的只有权值为 \(x\) 和 \(y\) 的数。
所以我们只更改他们的并查集关系,不直接重构整块。
时间复杂度不变,但足以通过此题了。
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
const int len = 430;
const int bas = 100000;
int n , m , top , a[N] , s[N] , h[N] , q[N] , l[N] , r[N] , ch[N] , fa[N] , pos[N] , stk[N];
struct Node
{
int a[N] , ton[N] , siz[len + 10];
}b[len + 100];
#define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
inline int read()
{
int asd = 0 , qwe = 1; char zxc;
while(!isdigit(zxc = getchar())) if(zxc == '-') qwe = -1;
while(isdigit(zxc)) asd = asd * 10 + zxc - '0' , zxc = getchar();
return asd * qwe;
}
inline int gf(int x)
{
return fa[x] == x ? x : fa[x] = gf(fa[x]);
}
inline int merge(int x , int y , int pos)
{
if(b[pos].ton[y]) fa[b[pos].ton[x]] = b[pos].ton[y];
else b[pos].ton[y] = b[pos].ton[x] , s[b[pos].ton[x]] = y;
b[pos].ton[x] = 0; return b[pos].a[x] - b[pos - 1].a[x];
}
inline void update(int x , int y , int ls , int rs)
{
int posl = pos[ls] , res = 0; top = 0;
b[posl].ton[x] = b[posl].ton[y] = 0;
for(int i = l[posl];i <= min(r[posl] , n);i++)
{
a[i] = s[gf(i)];
if(a[i] == x || a[i] == y) stk[++top] = i;
}
for(int i = ls;i <= rs;i++)
if(a[i] == x) res++ , a[i] = y;
for(int i = 1;i <= top;i++)
{
int j = stk[i];
if(!b[posl].ton[a[j]]) b[posl].ton[a[j]] = fa[j] = j , s[j] = a[j];
else fa[j] = b[posl].ton[a[j]];
}
for(int i = posl;i <= pos[n];i++)
b[i].siz[pos[x]] -= res , b[i].a[x] -= res,
b[i].siz[pos[y]] += res , b[i].a[y] += res;
}
inline void delet(int x) { h[x]-- , q[pos[x]]--; }
inline void insert(int x) { h[x]++ , q[pos[x]]++; }
int main()
{
int fir = 0;
n = read() , m = read();
for(int i = 1;i <= bas;i++) pos[i] = (i - 1) / len + 1 , r[pos[i]] = i;
for(int i = 1;i <= bas;i++) l[pos[i]] = (l[pos[i]] ? l[pos[i]] : i);
for(int i = 1;i <= n;i++) a[i] = read();
for(int i = 1;i <= pos[n];i++)
for(int j = l[i];j <= min(r[i] , n);j++)
b[i].a[a[j]]++ , b[i].siz[pos[a[j]]]++;
for(int i = 1;i <= pos[n];i++)
for(int j = l[i];j <= min(r[i] , n);j++)
if(!b[i].ton[a[j]]) b[i].ton[a[j]] = fa[j] = j , s[j] = a[j];
else fa[j] = b[i].ton[a[j]];
for(int i = 1;i <= pos[n];i++)
{
for(int j = 1;j <= bas;j++)
b[i].a[j] += b[i - 1].a[j];
for(int j = 1;j <= pos[bas];j++)
b[i].siz[j] += b[i - 1].siz[j];
}
for(int o = 1;o <= m;o++)
{
int opt = read();
if(opt == 1)
{
int ls = read() , rs = read() , x = read() , y = read();
if(x == y) continue;
if(pos[ls] == pos[rs]) update(x , y , ls , rs);
else
{
int res = 0;
update(x , y , ls , r[pos[ls]]) , update(x , y , l[pos[rs]] , rs);
for(int i = pos[ls] + 1;i <= pos[rs] - 1;i++)
ch[i] = merge(x , y , i);
for(int i = pos[ls] + 1;i <= pos[rs] - 1;i++)
res += ch[i] , b[i].a[x] -= res , b[i].a[y] += res,
b[i].siz[pos[x]] -= res , b[i].siz[pos[y]] += res;
for(int i = pos[rs];i <= pos[n];i++)
b[i].a[x] -= res , b[i].a[y] += res,
b[i].siz[pos[x]] -= res , b[i].siz[pos[y]] += res;
}
}
if(opt == 2)
{
int ls = read() , rs = read() , k = read() , now = 1 , res;
if(pos[ls] == pos[rs])
{
for(int i = ls;i <= rs;i++)
{
insert(s[gf(i)]);
}
for(int i = 1;i <= pos[bas];i++)
if(k <= q[i]) break;
else k -= q[i] , now++;
for(int i = l[now];i <= r[now];i++)
if(k <= h[i]) { res = i; break; }
else k -= h[i];
for(int i = ls;i <= rs;i++) delet(s[gf(i)]);
}
else
{
int bl = pos[ls] , br = pos[rs] - 1;
for(int i = ls;i <= r[pos[ls]];i++) insert(s[gf(i)]);
for(int i = l[pos[rs]];i <= rs;i++) insert(s[gf(i)]);
for(int i = 1;i <= pos[bas];i++)
if(k <= q[i] + b[br].siz[i] - b[bl].siz[i]) break;
else k -= q[i] + b[br].siz[i] - b[bl].siz[i] , now++;
for(int i = l[now];i <= r[now];i++)
if(k <= h[i] + b[br].a[i] - b[bl].a[i]) { res = i; break; }
else k -= h[i] + b[br].a[i] - b[bl].a[i];
for(int i = ls;i <= r[pos[ls]];i++) delet(s[gf(i)]);
for(int i = l[pos[rs]];i <= rs;i++) delet(s[gf(i)]);
}
printf("%d\n" , res);
}
}
return 0;
}