首页 > 其他分享 >【XSY3326】米缸(时间复杂度均衡,线段树,基环树,倍增)

【XSY3326】米缸(时间复杂度均衡,线段树,基环树,倍增)

时间:2022-10-30 11:14:47浏览次数:60  
标签:米缸 ny int 复杂度 nx 基环树 第一列 XSY3326

时间复杂度的均衡。

先考虑暴力的想法:显然这是一棵基环树,那么我们每次修改时暴力 \(O(nm)\) 重构基环树,然后询问的时候就能 \(O(1)\) 查询。时间复杂度 \(O(nmq)\)。

考虑均衡时间复杂度,我们考虑只记录第一列的每个点 \(i\) 走 \(m\) 步之后会绕回第一列的哪个点,设为 \(F_i\)。那么这棵基环树的大小就是 \(n\) 了。然后询问的时候只需要先 \(O(m)\) 暴力走到第一列再直接 \(O(1)\) 在基环树上走然后再 \(O(m)\) 走完剩下的步数。

思考修改也能不能同样降复杂度。

答案是可行的。假设修改了点 \((x,y)\) 的值,假设 \((x,y)\) 往后走会走到第一列的第 \(t\) 个位置。我们可以从第 \(x\) 列往前推当前列的哪些位置往后走所到达的第一列的位置被更改了(显然若被更改只会被更改为 \(t\)),发现这始终是一个区间:假设第 \(i+1\) 列是区间 \([l,r]\) 的位置被改为了 \(t\),那么当前第 \(i\) 列的 \([l+1,r-1]\) 的位置肯定也被更改为 \(t\),而位置 \(l-1,l,r,r+1\) 都有可能被更改为 \(t\),这四个位置直接暴力讨论即可。而且不可能存在 \(l-1\) 被改为了 \(t\) 而 \(l\) 不会改的情况,于是这始终是一个区间。最后推到第一列即为要更改的 \(F_i\)。然后我们再暴力 \(O(n)\) 重构基环树。

修改时间复杂度 \(O(n+m)\),询问时间复杂度 \(O(m)\)。

修改降复杂度的做法有点难看出来,下面有一个更加暴力的做法:

审视修改时我们要解决的问题:修改某一个位置 \((x,y)\) 的值,然后更新 \(F_i\),即求第一列每个点走完一圈后会回到第一列的哪个点。

直接上线段树。用线段树维护每一列每个点往后走一步会走到下一列哪个点,即线段树每个节点存一个大小为 \(n\) 的数组,然后合并的时候暴力 \(O(n)\) 合并。

那么在线段树中修改第 \(x-1\) 列,然后再更新 \(F_i\) 即可。

修改时间复杂度 \(O(n\log m)\),询问时间复杂度 \(O(m)\)。

然后你不想写基环树的话可以直接用倍增,修改时间复杂度 \(O(n(\log m+\log k))\),询问时间复杂度 \(O(m+\log k)\)。

写的是最后一种码量最小但时间复杂度最大做法:

#include<bits/stdc++.h>

#define N 2010
#define LN 31

using namespace std;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int n,m,q,a[N][N],f[N][3];
int bit[LN],F[N][LN];
int nxt[N<<2][N];

int nxtx(int x,int y)
{
	int nxty=y%m+1;
	int maxn=0,maxx;
	for(int i=0;i<3;i++)
	{
		if(a[f[x][i]][nxty]>maxn)
		{
			maxn=a[f[x][i]][nxty];
			maxx=f[x][i];
		}
	}
	return maxx;
}

void up(int k)
{
	for(int i=1;i<=n;i++)
		nxt[k][i]=nxt[k<<1|1][nxt[k<<1][i]];
}

void build(int k,int l,int r)
{
	if(l==r)
	{
		for(int i=1;i<=n;i++)
			nxt[k][i]=nxtx(i,l);
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	up(k);
}

void update(int k,int l,int r,int x)
{
	if(l==r)
	{
		for(int i=1;i<=n;i++)
			nxt[k][i]=nxtx(i,l);
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) update(k<<1,l,mid,x);
	else update(k<<1|1,mid+1,r,x);
	up(k);
}

void getF()
{
	for(int i=1;i<=n;i++)
		F[i][0]=nxt[1][i];
	for(int j=1;j<=30;j++)
		for(int i=1;i<=n;i++)
			F[i][j]=F[F[i][j-1]][j-1];
}

int main()
{
	bit[0]=1;
	for(int i=1;i<=30;i++) bit[i]=bit[i-1]<<1;
	n=read(),m=read();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			a[i][j]=read();
	for(int i=1;i<=n;i++)
		f[i][0]=(i-2+n)%n+1,f[i][1]=i,f[i][2]=i%n+1;
	build(1,1,m);
	getF();
	q=read();
	int nx=1,ny=1;
	while(q--)
	{
		char opt[10];
		scanf("%s",opt);
		if(opt[0]=='m')
		{
			int k=read();
			while(k>0&&ny!=1)
			{
				nx=nxtx(nx,ny);
				k--,ny=ny%m+1;
			}
			if(!k)
			{
				printf("%d %d\n",nx,ny);
				continue;
			}
			int div=k/m;
			k-=m*div;
			for(int i=30;i>=0;i--)
			{
				if(div-bit[i]>=0)
				{
					nx=F[nx][i];
					div-=bit[i];
				}
			}
			while(k>0)
			{
				nx=nxtx(nx,ny);
				k--,ny=ny%m+1;
			}
			printf("%d %d\n",nx,ny);
		}
		else
		{
			int x=read(),y=read();
			a[x][y]=read();
			update(1,1,m,(y-2+m)%m+1);
			getF();
		}
	}
	return 0;
}
/*
4 4
1 2 9 3
3 5 4 8
4 3 2 7
5 8 1 6
4
move 1
move 1
change 1 4 100
move 1
*/
/*
3 4
10 20 30 40
50 60 70 80
90 93 95 99
3
move 4	
change 2 1 100
move 4
*/

标签:米缸,ny,int,复杂度,nx,基环树,第一列,XSY3326
From: https://www.cnblogs.com/ez-lcw/p/16840749.html

相关文章