HDU 6756 2020 Multi-University Training Contest 1 1006 分块+均摊复杂思想 做到n * sqrt(n)复杂度

更新一下这题的n*sqet(n)解法。

我们发现:这题要么更新是O(n),查询是O(1),要么更新是O(1),查询是O(n).

我们可以采取均摊复杂度的思想,让查询和更新都做到O(sqrt(n)).

每次更新:若当前节点的度数小于等于根号n,那么则暴力更新其周围节点。否则不管,其影响留到查询时更新。

查询时:点x的周围节点,度数小于等于根号n的节点更新对x造成的影响已经更新。只需要考虑度数大于根号n的节点更新对x造成的影响。由于x周围度数大于根号n节点不会超过根号n个,所以此次更新影响也是根号n级别的。等查询完mex后,消除影响。

这样可以不用维护影响的变化。然后这样更新和查询都是根号n次。

我们用分块维护mex。把vs数组进行分块,显然mex表示从前往后,第一个不满的块中第一个vs为0的数。

所以我们只需要维护每个块是否为满即可。所以每次更新只需要让点val所在块的度数加减1即可,这里是O1更新的。

而最后查询时根号n即可。注意这里的查询根号n与更新根号n是独立的,即复杂度分开。

所以总复杂度为:O(n*sqrt(n)) !;

注意:vector重新定义空间最好用assign(n,a),开辟n个元素,并且都赋值为a,而resize赋值不会赋值已经存在的元素。因为这卡半天。。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const int M = 1e5+7;
vector<int>v[M],g[M];
struct Block {
  	vector<int>nm,vs,bel;
	vector<pair<int,int> >rg;
	int bs,sz,V;
	void build(int u)
	{
		V=g[u].size()+1;sz=sqrt(V+0.5);
		rg.clear();bs=0;vs.clear();
		nm.assign(V+1,0);bel.assign(V+1,0);
		for(int i=0;i<=V;i+=sz)
		{
			rg.emplace_back(i,min(i+sz-1,V));
			for(int j=i;j<=min(i+sz-1,V);j++)bel[j]=bs;
			++bs;
			vs.pb(0);
		}
	}


	void ins(int x)
	{
		if(x>V)return ;//mex不可能比V还大(因为最多V个数,mex最大为 V+1,有用数的最大为 V) 
		nm[x]++;
		if(nm[x]==1)vs[bel[x]]++;
	}
	void del(int x)
	{
		if(x>V)return ;
		nm[x]--;
		if(nm[x]==0)vs[bel[x]]--;
	}
  	int qu()
	{
		for(int i=0;i<bs;i++)
		{
			if(vs[i]==rg[i].second-rg[i].first+1)continue;//这一块满了,最小不存在整数不在这一块
			for(int j=rg[i].first;j<=rg[i].second;j++)
				if(!nm[j])return j;
		} 
		return V+1;
	}
} p[M];

//struct BLOCK{


//}p[M];
int a[M];
int main()
{

  	int T;
	cin>>T;
	while(T--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		int SZ=sqrt(n+0.5);
		for(int i=1;i<=n;i++)scanf("%d",&a[i]),g[i].clear(),v[i].clear();
		for(int i=1;i<=m;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			g[x].pb(y),g[y].pb(x);
		}
		for(int x=1;x<=n;x++)
		{
			p[x].build(x);
		//	cout<<"ojk1"<<endl;
			for(auto y:g[x])
			{
				if(g[y].size()>SZ)v[x].pb(y);
				else p[x].ins(a[y]);
			}
		}
		int q;
		scanf("%d",&q);
		while(q--)
		{
			int ty,x,y;
			scanf("%d",&ty);
			if(ty==1)
			{
				scanf("%d%d",&x,&y);
				if(g[x].size()<=SZ)
				{
					for(auto t:g[x])
					{
						p[t].del(a[x]);
						p[t].ins(y);
					}
				}
				a[x]=y;
			}
			else
			{
				scanf("%d",&x);
				for(auto t:v[x])
					p[x].ins(a[t]);//点x周围,size>SZ的点还未更新周围
				printf("%d\n",p[x].qu());
				for(auto t:v[x])
					p[x].del(a[t]);
			}
		}
	} 
	return 0;
}

猜你喜欢

转载自blog.csdn.net/bjfu170203101/article/details/107511812
今日推荐