告别

题目大意

一个n的排列,每次可以选择三个不同位置(这三个位置有序,(2,1,3)与(1,2,3)不同),然后对它们进行一次轮换。
只能操作m次,某一次操作过后当前排列变成了目标排列则停止操作。
每次均等概率选择三个有序位置进行操作。
在模意义下求m次操作内变成目标排列的概率。

设状态

首先可以扭转初始排列和目标排列,使得目标排列变成有序的(就是变成1,2,3……n)
我们知道可以用置换来表示一个排列,目标排列此时对应n个单位置换,同时n个单位置换也一定只对应目标排列。
对于两种置换,如果其中所有置换长度组成的多重集合完全相同,能拓展出的状态也完全相同,因此应该考虑缩到一起。
这样子状态只有135种。
暴力枚举每种状态,然后可以构造出与其对应的一个可行排列,再暴力枚举三个有序位置,然后再找会变成哪个状态(可以二分或map)
由于走到目标状态就停止操作,因此目标状态不能有连向其他状态的边,只能有一个自环(方便我们得到m次以内而不是恰好m次)
得到转移矩阵后可以做矩阵乘法。

#include<cstdio>
#include<algorithm>
#include<map>
#include<vector>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=150+10,mo=998244353;
typedef long long ll;
vector<int> now;
map<vector<int>,int> ha;
int c[maxn][20];
int dis[maxn][maxn],ans[maxn][maxn],o[maxn][maxn],sta[60],a[20],b[20],d[20],ts[20],p[20];
bool bz[20];
int i,j,k,l,t,n,m,sid,tid,tot,top,sum,cnt,w1,w2,w3,num;
int quicksortmi(int x,int y){
    if (!y) return 1;
    int t=quicksortmi(x,y/2);
    t=(ll)t*t%mo;
    if (y%2) t=(ll)t*x%mo;
    return t;
}
void dfs(int x,int dow,int y){
    if (!y){
        int i;
        top++;
        fo(i,1,x-1) c[top][i]=p[i];
        c[top][0]=x-1;
        now.clear();
        fo(i,1,x-1) now.push_back(p[i]);
        ha[now]=top;
        return;
    }
    int i;
    fo(i,dow,y){
        p[x]=i;
        dfs(x+1,i,y-i);
    }
}
int getid(){
    int i;
    now.clear();
    fo(i,1,cnt) now.push_back(d[i]);
    return ha[now];
}
int change(int x,int y,int z){
    int i;
    fo(i,1,n) b[i]=a[i];
    int t=b[x];
    b[x]=b[y];
    b[y]=b[z];
    b[z]=t;
    cnt=0;
    fo(i,1,n) bz[i]=0;
    fo(i,1,n)
        if (!bz[i]){
            t=0;
            j=i;
            while (!bz[j]){
                bz[j]=1;
                t++;
                j=b[j];
            }
            d[++cnt]=t;
        }
    sort(d+1,d+cnt+1);
    return getid();
}
int main(){
    freopen("goodbye.in","r",stdin);freopen("goodbye.out","w",stdout);
    scanf("%d%d",&n,&m);
    num=quicksortmi(n*(n-1)*(n-2),m);
    num=quicksortmi(num,mo-2);
    fo(i,1,n) scanf("%d",&a[i]);
    fo(i,1,n) scanf("%d",&b[i]);
    fo(i,1,n) ts[b[i]]=i;
    fo(i,1,n) a[i]=ts[a[i]],b[i]=i;
    dfs(1,1,n);
    fo(i,1,n)
        if (!bz[i]){
            t=0;
            j=i;
            while (!bz[j]){
                bz[j]=1;
                t++;
                j=a[j];
            }
            d[++cnt]=t;
        }
    sort(d+1,d+cnt+1);
    sid=getid();
    fo(i,1,n) d[i]=1;
    cnt=n;
    tid=getid();
    fo(i,1,top){
        j=1;k=1;
        while (j<=c[i][0]){
            fo(l,k,k+c[i][j]-2) a[l]=l+1;
            a[k+c[i][j]-1]=k;
            k+=c[i][j];
            j++;
        }
        fo(w1,1,n)
            fo(w2,1,n)
                fo(w3,1,n)  
                    if (w1!=w2&&w1!=w3&&w2!=w3){
                        j=change(w1,w2,w3);
                        if (i==1) j=1;
                        (dis[i][j]+=1)%=mo;
                    }
    }
    //dis[tid][n+1]=dis[n+1][n+1]=1;
    /*fo(i,1,n){
        fo(j,1,n)
            printf("%d ",dis[i][j]);
        printf("\n");
    }*/
    //m++;
    while (m){
        sta[++sum]=m%2;
        m/=2;
    }
    fo(i,1,top) ans[i][i]=1;
    while (sum){
        fo(i,1,top)
            fo(j,1,top)
                o[i][j]=0;
        fo(k,1,top)
            fo(i,1,top)
                fo(j,1,top)
                    o[i][j]=(o[i][j]+(ll)ans[i][k]*ans[k][j]%mo)%mo;
        fo(i,1,top)
            fo(j,1,top)
                ans[i][j]=o[i][j];
        if (sta[sum]){
            fo(i,1,top)
                fo(j,1,top)
                    o[i][j]=0;
            fo(k,1,top)
                fo(i,1,top)
                    fo(j,1,top)
                        o[i][j]=(o[i][j]+(ll)ans[i][k]*dis[k][j]%mo)%mo;
            fo(i,1,top)
                fo(j,1,top)
                    ans[i][j]=o[i][j];
        }
        sum--;
    }
    /*fo(i,1,n){
        fo(j,1,n)
            printf("%d ",ans[i][j]);
        printf("\n");
    }*/
    fo(i,1,top)
        fo(j,1,top)
            dis[i][j]=0;
    dis[1][sid]=1;
    fo(i,1,top)
        fo(j,1,top)
            o[i][j]=0;
    fo(k,1,top)
        fo(i,1,top)
            fo(j,1,top)
                o[i][j]=(o[i][j]+(ll)dis[i][k]*ans[k][j]%mo)%mo;
    fo(i,1,top)
        fo(j,1,top)
            ans[i][j]=o[i][j];
    printf("%d\n",(ll)ans[1][1]*num%mo);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值