0717果苣之手

简要题意:

一副牌有n张,编号为1,2,...,n。初始阶段牌按顺序排列(即位于i号未知的牌恰好是i号牌)。

我们会进行m次洗牌,每次洗牌会将原本位于位置i的牌洗到pi,而p1,p2,...,pn构成了一个排列

问最后发牌时的顺序。

分析:

由于pi是1到n的一种排列,所以我们可以知道:

洗牌,是有很多个环的!

换句话说,i号位置最后是哪张牌,在其构成的环内,有周期性。

 

以上图为例,n=4;p1=2;p2=3;p3=4;p4=1

经过一次洗牌,1号会换到4处,2号会换到1处,以此类推。

所以,它的周期为环内点的个数:4

我们处理时,只需要把每个这样的环单独处理,将 m%周期,然后DFS

 

#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=3e5+5;
int n, sum[N], fa[N];
int getf(const int x)
{
    if(fa[x]==x)return x;
    return fa[x]=getf(fa[x]);
}
int getnet,father,gt[N],to[N],Ans[N],check[N];
void DFS(const int x,const int id)
{
    check[x]=1;gt[id]=x;
    if(!check[to[x]])DFS(to[x],id+1);
    int tmp=id+getnet;if(tmp>sum[father])tmp-=sum[father];
    Ans[x]=gt[tmp];
}
char rdin[N];
map<int,int>mp;
signed main()
{
    scanf("%d",&n);scanf("%s",&rdin[1]);rdin[0]='0';
    for(re i=1;i<=n;++i)fa[i]=i,sum[i]=1;
    for(re i=1;i<=n;++i)
    {
        int x;scanf("%d",&x);to[x]=i;
        int f1=getf(i),f2=getf(x);
        if(f1!=f2)fa[f1]=f2,sum[f2]+=sum[f1];
    }
    for(re i=1;i<=n;++i)
    if(!check[i])
    {
        getnet=0;father=getf(i);
        if(!mp.count(sum[father]))
        {
            int tmp=0;
            for(re j=1,len=strlen(rdin)-1;j<=len;++j)
            tmp=((tmp<<1)+(tmp<<3)+(rdin[j]-'0'))%sum[father];
            mp[sum[father]]=tmp;
        }
        getnet=mp[sum[father]];
        DFS(i, 1);
    }
    for(re i=1;i<=n;++i)printf("%d ",Ans[i]);
    return 0;
}

 

值得注意的是,m<=10100000,对此我们只需要像“快读”一样,处理一下即可!

 

“读题马虎一时,爆零心痛一世!”----《kzsn语录》kzsn

 

posted @ 2021-07-18 23:12  kzsn  阅读(49)  评论(0)    收藏  举报