【洛谷3207】[HNOI2010] 物品调度(置换问题)

点此看题面

  • (n)个位置标号为(0sim n-1),初始位置(0)为空,位置(i)上放着编号为(i)的物品。
  • 给出空位的最终位置(pos_0),并给定一个序列(c)和一个常数(d)用于生成第(i)个物品的最终位置(pos_i):要求(pos_i=(c_i+d imes x_i+y_i)\%n)(pos_i)不与之前任意(pos_j)重复,且在此前提下首先(y_i)尽可能小,,其次(x_i)尽可能小。
  • 每次你可以把一个物品移到空位上,求至少多少次操作能把所有物品移动到对应的最终位置。
  • 数据组数(le20)(nle10^5)

最终位置的求解

考虑我们首先要满足(y_i)尽可能小,那么就是要找到最小的(y_i),使得存在一个((d imes x_i+(c_i+y_i))\%n)没有出现过。

(g=gcd(d,n)),则如果两数(a,b)满足(aequiv b(mod g)),它们((d imes k+r)\%n)的形式中的(r)就相等。

因此,我们考虑开一个(set),存下还有哪些(rin[0,g))仍然存在((d imes k+r)\%n),然后每次就是先尝试找到大于等于(c_i\%g)的最小的可行(r),找不到就找最小的可行(r)

现在确定了(r)(即确定了(y_i)),我们还要让(x_i)尽可能小。

我们事先预处理出每个数((d imes k+r)\%n)的形式中对应的(k),对于每一个(r)开一个(set)存下还有哪些(k),接下来的查询过程和上面就非常类似了,先尝试找到大于等于当前(k)的最小的可行(k),找不到就找最小的可行(k)

最少步数

(pos_i)看作一个置换,把原问题分解为若干个置换环。

如果某个置换环大小为(1),显然无需操作,代价为(0)

否则,如果只是简单给环转一遍代价为环上元素个数减(1)。但由于我们只能与空位交换,所以对于不包含空位的环,我们需要花费两次代价在最开始把一个元素换出去、在最后把一个元素换回来。

简单讨论一下即可。

代码:(O(Tnlogn))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
using namespace std;
int n,m,d,pos[N+5],r[N+5],k[N+5],vis[N+5];set<int> S,P[N+5];set<int>::iterator it,jt;
I int gcd(CI x,CI y) {return y?gcd(y,x%y):x;}
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	char oc,FI[FS],*FA=FI,*FB=FI;
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
int main()
{
	RI Tt,i,j,x,c,p,q,g,t;read(Tt);W(Tt--)
	{
		for(read(n,pos[0],q,p,m,d),g=gcd(n,d),i=0;i^n;++i) vis[i]=0;
		for(i=0;i^g;++i) for(S.insert(x=i),j=0;j^(n/g);x=(x+d)%n,++j) P[r[x]=i].insert(k[x]=j);//预处理,把每个数表示成dk+r
		P[r[pos[0]]].erase(k[pos[0]]),P[r[pos[0]]].empty()&&(S.erase(r[pos[0]]),0);//删去pos[0]
		for(c=0,i=1;i^n;++i) c=(1LL*c*q+p)%m,x=c%n,
			(it=S.lower_bound(r[x%d]))==S.end()&&(it=S.begin(),0),//找到最优的r
			(jt=P[*it].lower_bound(k[(x+(*it>=x%g?*it-x%g:*it-x%g+g))%n]))==P[*it].end()&&(jt=P[*it].begin(),0),//找到最优的k,注意先把x修改为模n余r的数
			pos[i]=(1LL*d*(*jt)+*it)%n,P[*it].erase(jt),P[*it].empty()&&(S.erase(it),0);//记录最终位置,然后删除
		for(t=i=0;i^n;++i) if(!vis[x=i]&&pos[i]^i) {c=-1;W(!vis[x]) ++c,vis[x]=1,x=pos[x];t+=c,i&&(t+=2);}printf("%d
",t);//枚举每个置换环简单讨论
	}return 0;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3207.html