这道题简直了...
码力极弱的我前前后后调了半个月...后来还是熬了一个通宵重构代码才A的
注意:
本篇题解内,值域与$n$同阶,皆表示为$O(n)$
思路:
看到Ynoi和$10^5$的数据范围,我们肯定会往分块的方向去想(
我们不难想到用分块来维护原数组的值,用$sum[i][j]$表示在前$i$块中,值为$j$的数量。
时间复杂度:修改$O(logn)$,询问$O(n)$,会被艹飞
其实此时修改的复杂度已经可以过了,但询问...
我们来康康我们是怎么询问的
我们在询问的时候,预处理了一个统计数组$total$,$total[i]\(记录了\)[l,r]$内值为$i$的个数,时间复杂度$O(n)$。
为了让其时间复杂度缩短至$O(logn)$,我们需要再分一次块,将值域分块。
那么我们就需要两个数组,$sum1[i][j]$表示在前$i$块中,值域在第$j$块的数的数量,$sum2[i][j]$同前面的$sum[i][j]$。
统计数组也开两个(其实一个就行)。
大家看到这里可能会有疑问:我怎么记录每个数的数值呢?
最直观的想法便是直接开一个数组记录下来。然而这样在修改的时候时间复杂度会加一个$n$,要不起(
怎么消掉这个$n$呢?我们有一个办法:每次只修改一个点,将与这个点值相同且在同一个块中的数连在这个点上。
对,就是并查集。
我们可以维护一个数组$rt[i][j]$,表示在第$i$块中,数值为$j$的我们所定义的这个根的位置(我语文不好不要介意)
然后就是并查集常规操作,$fa$数组。
最后我还开了一个$isrt[i]$数组,表示第$i$个数是否是它所在的块中某个数的根。如果他是$a$的根,则$isrt[i]=a$,否则$isrt[i]=0$。
P4119 = 分块 + 值域分块 + 并查集
实现:
让我们细化修改和询问操作。
修改:散块(即不完整的块)直接暴力把$x,y$的两个并查集拆了重建,整块就直接$\beginfa[rt[i][x]]=rt[i]y\rt[i][x]=rt[i]y\end$
即可。记得顺带维护一下$isrt,fa$就行。
询问没什么好说的,既然要查找第k大,我们就扫一遍所有值域块,如果第k大在这个值域块中,就暴力扫描该值域块。
具体来说,就是先预处理好之前说的$total$数组,整块就$total[i]+=sum1[r\text{所在的块}][i]-sum1[l\text{所在的块}-1][i]$,散块就$total[isrt[getf(j)]\text{所在的值域块}]++$就行了。暴力扫块也是依样画葫芦。
需要注意,当块长(不是值域块长)为$\sqrt$的时候会TLE翻车,$O2$都拉不回来qwq块长取$500-600$的时候时间复杂度较优,跑得飞快
我这里预处理了每一个数所在的块和每一个值所在的值域块,其实不要也可qwq
完结撒花花~
code:
#include<bits/stdc++.h>
#define rint register int
using namespace std;
int sum1[180][330] , sum2[180][101000] , rt[180][101000] , fa[101000] , p[101000] , np[101000] , tot;
int n , m , a , opt , l , r , x , y , k , num , siz = 600 , numc = 316 , sizc = 317 , isrt[101000];
int chan[101000] , total[400] , total2[400] , pa , pb;
inline int read()
{
int a = 0 , f = 0;
char c = getchar();
while(c < '0' || c > '9')
{
if(c == '-') f = 1;
c = getchar();
}
while(c >= '0' && c <= '9')
{
a = (a << 1) + (a << 3) + (c ^ 48);
c = getchar();
}
if(f)
a = -a;
return a;
}
inline int getf( int k )
{
if(fa[k] == k)
return k;
return fa[k] = getf(fa[k]);
}
void update( int pl , int l , int r , int x , int y )
{
int maxx = min(n , pl * siz);
tot = 0;
for(rint j = (pl - 1) * siz + 1 ; j <= maxx ; j++ )
{
if(isrt[getf(j)] == x)
{
fa[j] = j;
isrt[j] = x;
}
else if(isrt[getf(j)] == y)
{
fa[j] = j;
isrt[j] = y;
}
}
rt[pl][x] = rt[pl][y] = 0;
for(rint j = l ; j <= r ; j++ )
{
if(isrt[j] == x || isrt[j] == y)
{
if(isrt[j] == x)
tot++;
if(!rt[pl][y])
{
rt[pl][y] = j;
isrt[j] = y;
}
else
{
fa[j] = rt[pl][y];
isrt[j] = 0;
}
}
}
for(rint j = (pl - 1) * siz + 1 ; j < l ; j++ )
{
if(isrt[j] == x)
{
if(!rt[pl][x])
{
rt[pl][x] = j;
}
else
{
fa[j] = rt[pl][x];
isrt[j] = 0;
}
}
else if(isrt[j] == y)
{
if(!rt[pl][y])
{
rt[pl][y] = j;
}
else
{
fa[j] = rt[pl][y];
isrt[j] = 0;
}
}
}
for(rint j = r + 1 ; j <= maxx ; j++ )
{
if(isrt[j] == x)
{
if(!rt[pl][x])
{
rt[pl][x] = j;
}
else
{
fa[j] = rt[pl][x];
isrt[j] = 0;
}
}
else if(isrt[j] == y)
{
if(!rt[pl][y])
{
rt[pl][y] = j;
}
else
{
fa[j] = rt[pl][y];
isrt[j] = 0;
}
}
}
for(rint j = pl ; j <= num ; j++ )
{
sum1[j][np[x]] -= tot;
sum1[j][np[y]] += tot;
sum2[j][x] -= tot;
sum2[j][y] += tot;
}
}
int main()
{
// freopen("1.txt" , "r" , stdin);
// freopen("2.txt" , "w" , stdout);
n = read() , m = read();
num = (n - 1) / siz + 1;
int cnt = 1;
for(rint i = 1 ; i <= n ; i++ )
{
if(i > cnt * siz)
{
cnt++;
}
p[i] = cnt;
}
cnt = 1;
for(rint i = 1 ; i <= 100000 ; i++ )
{
if(i > cnt * sizc)
{
cnt++;
}
np[i] = cnt;
}
for(rint i = 1 ; i <= n ; i++ )
{
a = read();
if(!rt[p[i]][a])
{
rt[p[i]][a] = i;
isrt[i] = a;
fa[i] = i;
}
else
{
fa[i] = rt[p[i]][a];
isrt[i] = 0;
}
for(rint j = p[i] ; j <= num ; j++ )
{
sum1[j][np[a]]++;
sum2[j][a]++;
}
}
for(rint i = 1 ; i <= m ; i++ )
{
opt = read() , l = read() , r = read();
pa = p[l] , pb = p[r];
// cout << opt << " ";
if(opt == 1)
{
x = read() , y = read();
if(x == y)
continue;
if(pa == pb)
{
update(pa , l , r , x , y);
}
else
{
if((pa - 1) * siz + 1 != l)
{
update(pa , l , pa * siz , x , y);
pa++;
}
if(pb * siz != r)
{
update(pb , (pb - 1) * siz + 1 , r , x , y);
pb--;
}
chan[pa - 1] = 0;
for(rint j = pa ; j <= pb ; j++ )
{
if(rt[j][x])
{
if(rt[j][y])
fa[rt[j][x]] = rt[j][y] , isrt[rt[j][x]] = 0 , rt[j][x] = 0;
else
rt[j][y] = rt[j][x] , isrt[rt[j][x]] = y , rt[j][x] = 0;
}
chan[j] = chan[j - 1] + sum2[j][x] - sum2[j - 1][x];
}
for(rint j = pa ; j <= pb ; j++ )
{
sum1[j][np[x]] -= chan[j];
sum1[j][np[y]] += chan[j];
sum2[j][x] -= chan[j];
sum2[j][y] += chan[j];
}
// cout << chan[pb] << "qwq\n";
for(rint j = pb + 1 ; j <= num ; j++ )
{
sum1[j][np[x]] -= chan[pb];
sum1[j][np[y]] += chan[pb];
sum2[j][x] -= chan[pb];
sum2[j][y] += chan[pb];
}
}
}
else
{
bool f1 = 0 , f2 = 0;
k = read();
memset(total , 0 , sizeof(total));
memset(total2 , 0 , sizeof(total2));
if(pa == pb)
{
for(rint j = l ; j <= r ; j++ )
{
total[np[isrt[getf(j)]]]++;
}
}
else
{
if((pa - 1) * siz + 1 != l)
{
for(rint j = l ; j <= pa * siz ; j++ )
{
total[np[isrt[getf(j)]]]++;
}
pa++;
f1 = 1;
}
if(pb * siz != r)
{
for(rint j = (pb - 1) * siz + 1 ; j <= r ; j++ )
{
total[np[isrt[getf(j)]]]++;
}
pb--;
f2 = 1;
}
for(rint j = 1 ; j <= numc ; j++ )
{
total[j] += sum1[pb][j] - sum1[pa - 1][j];
}
}
for(rint j = 1 ; j <= numc ; j++ )
{
if(k <= total[j])
{
int minn = (j - 1) * sizc + 1 , maxx = j * sizc;
if(pa == pb)
{
for(rint i_ = l ; i_ <= r ; i_++ )
{
if(isrt[getf(i_)] >= minn && isrt[getf(i_)] <= maxx)
total2[isrt[getf(i_)] - minn + 1]++;
}
}
else
{
if(f1)
{
pa--;
for(rint i_ = l ; i_ <= pa * siz ; i_++ )
{
if(isrt[getf(i_)] >= minn && isrt[getf(i_)] <= maxx)
total2[isrt[getf(i_)] - minn + 1]++;
}
pa++;
}
if(f2)
{
pb++;
for(rint i_ = (pb - 1) * siz + 1 ; i_ <= r ; i_++ )
{
if(isrt[getf(i_)] >= minn && isrt[getf(i_)] <= maxx)
total2[isrt[getf(i_)] - minn + 1]++;
}
pb--;
}
for(rint i_ = minn ; i_ <= maxx ; i_++ )
{
total2[i_ - minn + 1] += sum2[pb][i_] - sum2[pa - 1][i_];
}
}
for(rint i_ = minn ; i_ <= maxx ; i_++ )
{
if(k <= total2[i_ - minn + 1])
{
printf("%d\n" , i_);
break;
}
else
k -= total2[i_ - minn + 1];
}
break;
}
else
k -= total[j];
}
}
}
return 0;
}