zoukankan      html  css  js  c++  java
  • 【洛谷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;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    Date日期对象
    JAVA适配器
    java 对象的多态性
    简单轮播
    ecshop 教程地址
    瀑布流js排列
    phpcms 搜索结果页面栏目不显示解决 方法
    手机自动跳转
    字串符转换数字、取小数点后两位数的方法
    js 判断鼠标进去方向
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3207.html
Copyright © 2011-2022 走看看