专栏文章

题解:P3207 [HNOI2010] 物品调度

P3207题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mimzt2ya
此快照首次捕获于
2025/12/01 18:14
3 个月前
此快照最后确认于
2025/12/01 18:14
3 个月前
查看原文

P3207 [HNOI2010] 物品调度

题意

题面有点令人犯恶心,先理清楚:
首先有个需要你生成的数组 cc,生成所需变量是用题目给你的:ppqqmm
生成规则是:c0=0c_0=0ci+1=(ci×q+p)c_{i+1}=(c_i \times q + p) modmod mm
每个盒子还有一个最终位置为posipos_iposi=(ci+d×xi+yi)pos_i=(c_i+d \times x_i+y_i) modmod nn。注意,这个 xix_iyiy_i 你是不知道的,要你自己生成。满足首先 yy 的字典序要最小,其次是 xx 的字典序也要小。
每次移动可以将某一个盒子移到空位上,问把所有的盒子移动到最终位置所需的最少步数。

解法

其实比较容易可以发现,增加 xx 会使得整个序列在操作的时候变成一个环,环与环之间的关系又要由 yy 来决定。此时就可以使用并查集进行优化,把使用了的位置连向空位。
求答案的时候就需要判断一下这个置换环内有没有空格,如果有空格就是环内元素个数减 11,否则加 11。什么意思呢?如下图:

注意的点

有一些小细节可以根据代码理解。

code

CPP
#include<bits/stdc++.h>
#define int long long
#define N 100005
using namespace std;
bool vis[N];
int T,n,s,p,q,m,d,ans,a[N],c[N],fa[N],pos[N];
int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));}
signed main(){
    scanf("%lld",&T);
    while(T--){
        scanf("%lld%lld%lld%lld%lld%lld",&n,&s,&p,&q,&m,&d);
        for(int i=0;i<n;i++) a[i]=0,fa[i]=i,vis[i]=0;
        vis[pos[0]=s]=1,ans=c[0]=0,fa[s]=(s+d)%n;
        for(int i=1;i<n;i++) c[i]=(c[i-1]*p+q)%m;
        for(int i=1;i<n;i++){
            int y=0;
            while(vis[find((y+c[i])%n)]) y++;
            pos[i]=find((y+c[i])%n);
            vis[pos[i]]=1,fa[pos[i]]=find((pos[i]+d)%n);
        }
        for(int i=0;i<n;i++) if(!a[i]&&pos[i]!=i){
            bool pd=0;
			int p=i,cnt=0;
			while(!a[p]){
				if(!p) pd=1;
                a[p]=1,p=pos[p],cnt++;
			}
            ans+=cnt+(pd?-1:1);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...