Loading

ABC232

E

Content

给一个 \(H\times W\) 的棋盘,\((x_1,y_1)\) 上有一个车(一次可以走到所在横行竖列的任意一格),求它经过 \(K\) 步走到 \((x_2,y_2)\) 的方案数对 \(998244353\) 取模。

Sol

对每一步单独考虑。

分为:

\(S_{0,0}=\{(x_1,y_1)\}\)

\(S_{0, 1} = \{ (x_1, y) \, \vert \, y \neq y_1 \}\)

\(S_{1, 0} = \{ (x, y_1) \, \vert \, x \neq x_1 \}\)

\(S_{1, 1} = \{ (x, y) \, \vert \, x \neq x_1 \land y \neq y_1 \}\)

四种情况,每种情况集合里的所有点的方案数都是相同的。

\(g_{k,a,b}\) 为情况 \(S_{a,b}\) 的方案数,有:

\(g_{k,0,0}=g_{k−1,0,1}+g_{k−1,1,0}\)

\(g_{k, 0, 1} = (M - 1) g_{k - 1, 0, 0} + (M - 2) g_{k - 1, 0, 1} + g_{k - 1, 1, 1}\)

\(g_{k, 1, 0} = (H - 1) g_{k - 1, 0, 0} + (H - 2) g_{k - 1, 1, 0} + g_{k - 1, 1, 1}\)

\(g_{k, 1, 1} = (H - 1)g_{k - 1, 0, 1} + (M - 1)g_{k - 1, 1, 0} + (H - 2 + M - 2) g_{k - 1, 1, 1}\)

\((x_2,y_2)\) 在集合 \(S_{a,b}\) 内,则答案为 \(\frac{g_{k,a,b}}{|S_{a,b}|}\)

这就是个矩阵幂,可以用矩阵加速优化至 \(O(logK)\)

Code

//O(K)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
int n,m,K;
int g[2][2][2];
inline int qpow(int x,int y,int res=1){
    for(;y;y>>=1){
        if(y&1) res=res*x%mod;
        x=x*x%mod;
    }
    return res;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>m>>K;
    int cnt[2][2]={{1,m-1},{n-1,n*m-n-m+1}};
    g[0][0][0]=1;
    for(int i=1;i<=K;++i){
        g[i&1][0][0]=(g[!(i&1)][0][1]+g[!(i&1)][1][0])%mod;
        g[i&1][0][1]=((m-1)*g[!(i&1)][0][0]%mod+(m-2)*g[!(i&1)][0][1]%mod+g[!(i&1)][1][1])%mod;
        g[i&1][1][0]=((n-1)*g[!(i&1)][0][0]%mod+(n-2)*g[!(i&1)][1][0]%mod+g[!(i&1)][1][1])%mod;
        g[i&1][1][1]=((n-1)*g[!(i&1)][0][1]%mod+(m-1)*g[!(i&1)][1][0]%mod+(n-2+m-2)*g[!(i&1)][1][1]%mod)%mod;
    }
    int x1,x2,y1,y2;
    cin>>x1>>y1>>x2>>y2;
    bool a=x1!=x2,b=y1!=y2;
    cout<<g[K&1][a][b]*qpow(cnt[a][b]%mod,mod-2)%mod;
    return 0;
}

关于矩阵快速幂做法,目标矩阵如下

\[\begin{bmatrix} g_{0,0} & g_{0,1} & g_{1,0} & g_{1,1} \end{bmatrix} \]

构造如下初始矩阵

\[\begin{bmatrix} 0 & m-1 & n-1 & 0\\ 1 & m-2 & 0 & n-1\\ 1 & 0 & n-2 & m-1\\ 0 & 1 & 1 & n-2+m-2 \end{bmatrix} \]

//O(logK)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=4,mod=998244353;
int n,m,K;
struct mat{int r,c,a[N][N];};
mat operator *(mat a,mat b){
    mat c;memset(&c,0,sizeof(c));
    for(int i=0;i<a.r;++i)
        for(int j=0;j<b.c;++j)
            for(int k=0;k<a.c;++k)
                c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j]%mod)%mod;
    c.r=a.r,c.c=b.c;
    return c;
}
inline int qpow(int x,int y,int res=1){
    for(;y;y>>=1){
        if(y&1) res=res*x%mod;
        x=x*x%mod;
    }
    return res;
}
inline void qpow(){
    int y;
    cin>>y;
    int G[4][4]={{0,m-1,n-1,0},{1,m-2,0,n-1},{1,0,n-2,m-1},{0,1,1,n-2+m-2}};
    mat res,x,ans;
    memset(&ans,0,sizeof(ans));
    memset(&x,0,sizeof(x));
    memset(&res,0,sizeof(res));
    x.r=4,x.c=4;
    res.r=4,res.c=4;
    for(int i=0;i<4;++i){
        res.a[i][i]=1;
        for(int j=0;j<4;++j) x.a[i][j]=G[i][j];
    }
    for(;y;y>>=1){
        if(y&1) res=res*x;
        x=x*x;
    }
    ans.r=1,ans.c=4;
    ans.a[0][0]=1;
    ans=ans*res;
    int x1,y1,x2,y2;
    cin>>x1>>y1>>x2>>y2;
    int a=((x1!=x2)<<1)+(y1!=y2);
    int cnt=(x1!=x2?n-1:1)*(y1!=y2?m-1:1)%mod;
    cout<<ans.a[0][a]*qpow(cnt,mod-2)%mod;
}
signed main(){
    cin>>n>>m;
    qpow();
    return 0;
}

F

Content

给两个长度为 \(N\) 的序列 \(A,B\),对于序列 \(A\) 有如下两种操作:

  • 选一个 \(i \in [1,N]\),花费 \(X\) 日元使 \(A_i \pm 1\)
  • 选一个 \(i\in[1,N-1]\),花费 \(Y\) 日元交换 \(A_i,A_{i+1}\)

求最少花费多少钱使得 \(A,B\) 序列相同。

Sol

我们可以把所有单点修改操作放到一起处理,把所有的交换操作放到一起处理,这样是无影响的,且确定了一个位置,以后都不会改它。

我们设最终答案处理完交换操作后的顺序由 \(A_1,A_2,...,A_N\) 变为了 \(A_{P_1},A_{P_2},...A_{P_N}\)\(P\)\([1,N]\) 的一个排列,每次可以交换相邻两个数,那么最少只用交换 \(P\) 的逆序对个数次(一个套路的结论)

考虑把逆序对的贡献分到每个位置上,方便我们和单点修改一起按位考虑算贡献。假设我们按下标从小到大确定每个位置,不难发现当前要确定的这一位对逆序对的贡献为:这一位放的位置之前有几个还没有放的(这些位置只能是之后放入的更大的元素,又因为这些位置比当前位置前,所以构成逆序对)。

于是做法就来了,设 \(S\) 为一个 \(n\) 位的二进制数,为 \(1\) 的位表示这一位被确定了,反之没确定,\(f(S)\) 表示状态为 \(S\) 的最小花费。

我们按从小到大的顺序放元素 \(P\) 中,枚举它要放到哪个位置,设函数 \(inv(S,i)\) 为状态 \(S\) 中前 \(i-1\) 位中有多少位没被确定,则有:

\(f(S')=f(S)+|A_i-B_{now}|\times X+inv(S,i)\times Y\)

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int INF=1e18;
int n,X,Y;
int a[20],b[20];
int f[1<<18];
inline int inv(int S,int x,int res=0){
    for(int i=1;i<x;++i) res+=!(S&(1<<i-1));
    return res;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>X>>Y;
    for(int i=1;i<=n;++i) cin>>a[i];
    for(int i=1;i<=n;++i) cin>>b[i];
    f[0]=0;
    for(int i=1;i<1<<n;++i) f[i]=INF;
    for(int i=0;i<1<<n;++i){
        int cnt=__builtin_popcount(i);
        for(int j=1;j<=n;++j){
            if(i&(1<<j-1)) continue;
            f[i|(1<<j-1)]=min(f[i|(1<<j-1)],f[i]+abs(a[j]-b[cnt+1])*X+inv(i,j)*Y);
        }
    }
    cout<<f[(1<<n)-1];
    return 0;
}
posted @ 2021-12-25 08:20  Quick_Kk  阅读(160)  评论(0编辑  收藏  举报