把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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

点此看题面

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

最终位置的求解

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

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

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

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

我们事先预处理出每个数\((d\times 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\n",t);//枚举每个置换环简单讨论
	}return 0;
}
posted @ 2021-05-21 11:22  TheLostWeak  阅读(66)  评论(0编辑  收藏  举报