luoguP4119 【[Ynoi2018]未来日记】

这道题简直了...

码力极弱的我前前后后调了半个月...后来还是熬了一个通宵重构代码才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;
}

转载于:https://www.cnblogs.com/lost-in-tianyi/p/11349599.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值