【洛谷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;
}
待到再迷茫时回头望,所有脚印会发出光芒