1. 程式人生 > >【BZOJ1998】[HNOI2010]物品調度(並查集,模擬)

【BZOJ1998】[HNOI2010]物品調度(並查集,模擬)

turn show else int 維護 sin size problem pro

【BZOJ1998】[HNOI2010]物品調度(並查集,模擬)

題面

BZOJ,為啥這題都是權限題啊?
洛谷

題解

先不管\(0\)位置是個空,把它也看成一個箱子。那麽最終的答案顯然和置換循環節的個數相關,對於大小為\(1\)的循環,顯然就是不要動。對於大小為\(L\)的循環,如果包含\(0\)位置,顯然還要動\(L-1\)次,如果不包含\(0\)位置,顯然要先把\(0\)位置弄進這個環裏面來,再把它移出去,也就是\(L+1\)次。那麽我們就可以很容易的計算答案。
然而現在最大的難題變成了如何計算最終位置了。
對於每個位置,我們不難發現隨著\(x\)的增大,關於\(n\)的余數一定是一個個的環。那麽我們考慮枚舉\(y\)

值,檢查\(y\)固定時,對應的這個環上是否還有空位,如果有就放進去。這個空位可以用並查集維護,每次將當前放完的位置指向下一個位置,即\(+d\)的位置。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 100100
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int C[MAX],pos[MAX];
int n,S,Q,P,M,D;
int f[MAX];bool vis[MAX];
int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);}
void merge(int u,int v){f[getf(u)]=getf(v);}
int main()
{
    int T=read();
    while(T--)
    {
        n=read();S=read();Q=read();P=read();M=read();D=read();
        for(int i=1;i<n;++i)C[i]=(1ll*C[i-1]*Q+P)%M;
        for(int i=0;i<n;++i)f[i]=i;
        memset(vis,0,sizeof(vis));
        pos[0]=S;vis[S]=true;merge(S,(S+D)%n);
        for(int i=1;i<n;++i)
        {
            int x=getf(C[i]%n),y=0;
            while(vis[x])++y,x=getf((C[i]+y)%n);
            pos[i]=x;vis[x]=1;merge(x,(x+D)%n);
        }
        memset(vis,0,sizeof(vis));int ans=0,gr=0;
        for(int i=0;i<n;++i)
            if(!vis[i])
            {
                int tot=0,u=i;
                while(!vis[u])++tot,vis[u]=true,u=pos[u];
                if(tot>1)ans+=tot,++gr;
            }
        if(!pos[0])ans+=gr;else ans+=gr-2;
        printf("%d\n",ans);
    }
    return 0;
}

【BZOJ1998】[HNOI2010]物品調度(並查集,模擬)