[USACO21JAN] Dance Mooves S

洛谷题面

题目大意

给定一个长度为 \(N\) 的序列 \(A\),初始化 \(A_i=i\)

随后 \(\rm Farmer~John\) 会给出 \(K\) 对指令,形式为 \(x_i,y_i\),表示将 \(A[x_i]\)\(A[y_i]\) 进行交换。

尽管给出了 \(K\) 对指令,但是奶牛们需要无穷止地跳下去,第 \(i\) 分钟需要执行第 \((i+K)\mod K\) 个操作(第 \(aK\) 秒执行第 \(K\) 个操作)。

求每头奶牛将会在哪些位置出现。

题目分析

这是我们一场模拟赛的第二题,赛时只想到了暴力循环点的做法。并且因为数组开大了直接挂完 (我以为用了 \(\rm bitset\) 再应该可以卡过去的)

\(\rm 50\) 分做法

通过手动枚举可以发现当奶牛们变了 \(N\times K\) 次之后,会在此回到初始时的序列。于是我们枚举每头牛在这 \(N\times K\) 次舞列中哪些位置出现过即可。

在枚举的过程中,可以开个桶来记录。但是不要像我一样把桶开炸了。。。

代码就不放了。

满分做法

假设小牛 \(A\)\(i\) 位置,小牛 \(B\)\(j\) 位置。

出现过操作 \(\rm i,f\)\(\rm j,i\),那么 \(K\) 分钟之后,\(B\) 也会到达 \(i\) 位置,它们走过的位置一定相同,这启发我们把环剥离出来或者使用并查集维护。

\(\rm vector\) 来记录每头奶牛出现过的点,再用 \(\rm set\) 统计每头奶牛出现的数量。

这里是并查集做法。

代码

const int ma=100005;

int a[ma],fa[ma];

int n,k;

vector<int>vec[ma];

set<int>ans[ma];

//==========并查集========
inline void init()
{
	for(register int i=1;i<=n;i++)
	{
		fa[i]=a[i]=i;
	}
}

inline int get(int x)
{
	if(fa[x]==x)
	{
		return x;
	}
	
	return fa[x]=get(fa[x]);
}

inline void merge(int x,int y)
{
	int f1=get(x),f2=get(y);
	
	if(f1!=f2)
	{
		fa[f1]=f2;
	}
}
//========================

int main(void)
{
	n=read(),k=read();
	
	init();
	
	for(register int i=1;i<=k;i++)
	{
		int x=read(),y=read();
		
		swap(a[x],a[y]);
		
		vec[a[x]].push_back(y);
		
		vec[a[y]].push_back(x);
	}
	
	for(register int i=1;i<=n;i++)
	{
		vec[a[i]].push_back(i);
	}
	
	for(register int i=1;i<=n;i++)
	{
		merge(i,a[i]);
	}
	
	for(register int i=1;i<=n;i++)
	{
		for(register int j=0;j<vec[a[i]].size();j++)
		{
			ans[get(a[i])].insert(vec[a[i]][j]);
		}
	}
	
	for(register int i=1;i<=n;i++)
	{
		printf("%d\n",ans[get(i)].size());
	}
	
	return 0;
}
posted @ 2021-10-06 12:53  Coros_Trusds  阅读(97)  评论(0)    收藏  举报